YMLiang

条件语句 select

select 语句
select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。

select 是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。
select 随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。

语法:

1
2
3
4
5
6
7
8
9
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}

函数

见Go开发基础入门——2

方法

方法只要记住有method 和 receiver即可

receiver可以是值也可以是指针

定义方法:

func (recevier  recevier's type) methodName(参数列表)(返回值列表){}

我们来测试一下是否只可以用 recevier's type 类型来使用这个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func (t T) testT() {
fmt.Println("类型 T 方法集包含全部 receiver T 方法")
}
func (s *S) testS() {
fmt.Println("类型 *S 方法集包含全部 receiver S + *S 方法")
}

type T struct {
int
}

type S struct {
int
}
func main() {
a := T{10}
b := &a
a.testT()
b.testT()

c := S{20}
d := &c
c.testS()
d.testS()

}

>类型 T 方法集包含全部 receiver T 方法
>类型 T 方法集包含全部 receiver T 方法
>类型 *S 方法集包含全部 receiver S + *S 方法
>类型 *S 方法集包含全部 receiver S + *S 方法

引用类型

变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配。通过GC回收。
获取指针类型所指向的值,使用:" * " 取值符号 。比如:var *p int, 使用*p获取p指向的值
指针、slice、map、chan等都是引用类型

new和make的区别
make 用来创建map、slice、channel
new 用来创建值类型

new 和 make 均是用于分配内存:
new 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new(type)、make(type)。new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针。它也可以被用于基本类型:v := new(int)。
make(T) 返回类型 T 的初始化之后的值,因此它比 new 进行更多的工作。new() 是一个函数,不要忘记它的括号。

切片

golang slice data[:6:8] 两个冒号的理解

常规slice , data[6:8],从第6位到第8位(返回6, 7),长度len为2, 最大可扩充长度cap为4(6-9)

另一种写法: data[:6:8] 每个数字前都有个冒号, slice内容为data从0到第6位,长度len为6,最大扩充项cap设置为8

a[x:y:z] 切片内容 [x:y] 切片长度: y-x 切片容量:z-x
1
2
3
4
5
6
7
8
9
10
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
d1 := slice[6:8]
fmt.Println(d1, len(d1), cap(d1))
d2 := slice[:6:8]
fmt.Println(d2, len(d2), cap(d2))
}

[6 7] 2 4
[0 1 2 3 4 5] 6 8

map

var map变量名 map[key] value
其中:key为键类型,value为值类型
例如:value不仅可以是标注数据类型,也可以是自定义数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
常用的初始化方法:
make()
Go语言提供的内置函数make()可以用于灵活地创建map。
预先给 make 函数一个合理元素数量参数,有助于提升性能。因为事先申请一大块内存,可避免后续操作时频繁扩张

type One struct {
name string
age int
}

func main(){
dataMap := make(map[string]One)
dataMap["1"] = One{"liangfeifan",22}
}


map[1:{liangfeifan 22}]
1
2
3
4
5
6
常规初始化方法:

dataMap := map[string]One{
"3": {"liang", 22},
"4": {"fei", 10},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
map操作:
插入、更新、查找、删除、判断是否存在、求长度

package main

import (
"fmt"
)

func main() {

m := map[string]string{"key0": "value0", "key1": "value1"}
fmt.Printf("map m : %v\n", m)
//map插入
m["key2"] = "value2"
fmt.Printf("inserted map m : %v\n", m)
//map修改
m["key0"] = "hello world!"
fmt.Printf("updated map m : %v\n", m)
//map查找
val, ok := m["key0"]
if ok {
fmt.Printf("map's key0 is %v\n", val)
}

// 长度:获取键值对数量。
len := len(m)
fmt.Printf("map's len is %v\n", len)

// cap 无效,error
// cap := cap(m) //invalid argument m (type map[string]string) for cap
// fmt.Printf("map's cap is %v\n", cap)

// 判断 key 是否存在。
if val, ok = m["key"]; !ok {
fmt.Println("map's key is not existence")
}

// 删除,如果 key 不存在,不会出错。
if val, ok = m["key1"]; ok {
delete(m, "key1")
fmt.Printf("deleted key1 map m : %v\n", m)
}

}
输出结果:

map m : map[key0:value0 key1:value1]
inserted map m : map[key0:value0 key1:value1 key2:value2]
updated map m : map[key0:hello world! key1:value1 key2:value2]
map's key0 is hello world!
map's len is 3
map's key is not existence
deleted key1 map m : map[key0:hello world! key2:value2]
map遍历:

不能保证迭代返回次序,通常是随机结果,具体和版本实现有关。

package main

import (
"fmt"
)

func main() {
m := make(map[int]int)
for i := 0; i < 10; i++ {
m[i] = i
}

fmt.Println(m)
fmt.Println(m)

for j := 0; j < 2; j++ {
fmt.Println("---------------------")
for k, v := range m {
fmt.Printf("key -> value : %v -> %v\n", k, v)
}
}
}
输出结果:

map[6:6 8:8 2:2 1:1 3:3 4:4 5:5 7:7 9:9 0:0]
map[2:2 6:6 8:8 5:5 7:7 9:9 0:0 1:1 3:3 4:4]
---------------------
key -> value : 2 -> 2
key -> value : 6 -> 6
key -> value : 8 -> 8
key -> value : 9 -> 9
key -> value : 0 -> 0
key -> value : 1 -> 1
key -> value : 3 -> 3
key -> value : 4 -> 4
key -> value : 5 -> 5
key -> value : 7 -> 7
---------------------
key -> value : 8 -> 8
key -> value : 2 -> 2
key -> value : 6 -> 6
key -> value : 3 -> 3
key -> value : 4 -> 4
key -> value : 5 -> 5
key -> value : 7 -> 7
key -> value : 9 -> 9
key -> value : 0 -> 0
key -> value : 1 -> 1
slice与map操作(slice of map)

package main

import (
"fmt"
)

func main() {
items := make([]map[int]int, 5)
for i := 0; i < 5; i++ {
items[i] = make(map[int]int)
}
items[0][0] = 0
items[1][2] = 3
fmt.Println(items)
}
输出结果:

[map[0:0] map[2:3] map[] map[] map[]]
map排序:

先获取所有key,把key进行排序,再按照排序好的key,进行遍历。

package main

import (
"fmt"
"sort"
)

func main() {
m := map[string]string{"q": "q", "w": "w", "e": "e", "r": "r", "t": "t", "y": "y"}
var slice []string
for k, _ := range m {
slice = append(slice, k)
}
fmt.Printf("clise string is : %v\n", slice)
sort.Strings(slice[:])
fmt.Printf("sorted slice string is : %v\n", slice)
for _, v := range slice {
fmt.Println(m[v])
}
}
输出结果:

clise string is : [e r t y q w]
sorted slice string is : [e q r t w y]
e
q
r
t
w
y
map反转:
初始化另外一个map,把key、value互换即可.

package main

import (
"fmt"
)

func main() {
m := map[int]string{1: "x", 2: "w", 3: "e", 4: "r", 5: "t", 6: "y"}
fmt.Println(m)
m_rev := make(map[string]int)
for k, v := range m {
m_rev[v] = k
}
fmt.Println(m_rev)
}
输出结果:

map[3:e 4:r 5:t 6:y 1:x 2:w]
map[r:4 t:5 y:6 x:1 w:2 e:3]
从 map 中取回的是一个 value 临时复制品,对其成员的修改是没有任何意义的。

package main

import (
"fmt"
)

func main() {
m := map[int]string{1: "x", 2: "w"}
fmt.Println(m)
for k, v := range m {
m[k] = v + v //修改map的值
v = v + "copy" //临时复制品,修改无效
}
fmt.Println(m)
}
输出结果:


容器和结构体(map and struct)

语法比较:
map[type]struct
map[type]*struct
package main

import "fmt"

func main() {
type user struct{ name string }
/*
当 map 因扩张而重新哈希时,各键值项存储位置都会发生改变。
因此,map 被设计成 not addressable。
类似 m[1].name 这种期望透过原 value 指针修改成员的行为自然会被禁 。
*/
m := map[int]user{ //

1: {"user1"},
}
// m[1].name = "Tom"
// ./main.go:16:12: cannot assign to struct field m[1].name in map
fmt.Println(m)

// 正确做法是完整替换 value 或使用指针。
u := m[1]
u.name = "Tom"
m[1] = u // 替换 value。

m2 := map[int]*user{
1: &user{"user1"},
}

m2[1].name = "Jack" // 返回的是指针复制品。透过指针修改原对象是允许的。
fmt.Println(m2)
}
输出结果:

map[1:{user1}]
map[1:0xc42000e1e0]
可以在迭代时安全删除键值。但如果期间有新增操作,那么就不知道会有什么意外了。

package main

import "fmt"

func main() {
for i := 0; i < 5; i++ {
m := map[int]string{
0: "a", 1: "a", 2: "a", 3: "a", 4: "a",
5: "a", 6: "a", 7: "a", 8: "a", 9: "a",
}

for k := range m {
m[k+k] = "x"
delete(m, k)
}

fmt.Println(m)
}
}
输出:
//每次输出都会变化

map[36:x 28:x 32:x 2:x 8:x 10:x 12:x]
map[12:x 6:x 16:x 28:x 4:x 10:x 72:x]
map[12:x 14:x 16:x 18:x 20:x]
map[18:x 10:x 14:x 4:x 6:x 16:x 24:x]
map[12:x 16:x 4:x 40:x 14:x 18:x]

管道 (channel)

  • Golang 引用类型 channel 是 CSP 模式的具体实现,用于多个 goroutine 通讯。其内部实现了同步,确保并发安全。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
channel概念:
a. 类似unix中管道(pipe)
b. 先进先出
c. 线程安全,多个goroutine同时访问,不需要加锁
d. channel是有类型的,一个整数的channel只能存放整数

channel声明:
var 变量名 chan 类型

package main

var ch0 chan int
var ch1 chan string
var ch2 chan map[string]string

type stu struct{}

var ch3 chan stu
var ch4 chan *stu

func main() {

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
channel初始化:
使用make进行初始化,比如:

ch := make(chan string,1)
ch2 := make(chan string)

上述两种:分别为 有缓冲,无缓冲
无缓冲的与有缓冲channel有着重大差别,那就是一个是同步的 一个是非同步的。

比如

c1:=make(chan int) 无缓冲

c2:=make(chan int,1) 有缓冲

c1<-1

无缓冲: 不仅仅是向 c1 通道放 1,而是一直要等有别的携程 <-c1 接手了这个参数,那么c1<-1才会继续下去,要不然就一直阻塞着。

有缓冲: c2<-1 则不会阻塞,因为缓冲大小是1(其实是缓冲大小为0),只有当放第二个值的时候,第一个还没被人拿走,这时候才会阻塞。

缓冲区是内部属性,并非类型构成要素。

请关注我的个人博客

 评论


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本站使用 Material X 作为主题 , 总访问量为 次 。
Copyright 2018-2019 YMLiang'BLOG   |   京ICP备 - 19039949  |  载入天数...载入时分秒...