一.map相关
1.map读取
如果key不存在,返回零值;也就是说,根据这个value的类型,返回缺省值,比如string返回空字符串,int返回0;
2.在range循环里对maps做添加或删除元素的操作是不安全的
如果在循环中对maps添加了一个元素,那么这个元素并不一定会出现在后续的迭代中。
在迭代时安全删除键值。但如果期间有新增操作,那么就不知道会有什么意外了。
对于第一点,我们知道maps实际上是结构体的指针;循环开始前,只会复制指针而不是内部的数据结构,
因此在循环中添加或删除元素所操作的内存空间和原变量一致,合情合理;
那为什么在后续的迭代中不一定能遍历到当前添加的元素?
如果你知道哈希表是如何工作的(map本质上就是哈希表),就会明白哈希表内部数组里的元素并不是以特定顺序存放;
最后一个添加的元素有可能经过哈希后被放到了内部数组里的第一个索引位,我们确实没有办法预测当前添加的元素
是否会出现在后续的迭代中,毕竟在添加元素的时候很可能已经遍历过了第一个索引位;
因此,当前添加的元素是否能在后续迭代中遍历到,还是看编译器的心情吧;
3.遍历map顺序随机
Go中map的一个很重要的特性就是:
当您多次通过range循环来迭代访问map中元素时,尽管您访问的是同一个map,
但是访问元素的顺序在前后两次range中是不会完全相同的,当然也不是完全随机的;
从Go1开始,Go在range遍历Map中元素的时候,从随机的一个位置开始迭代;
为什么要这样做?
因为Go的设计者们认为会有一些程序员对同一个map中元素的遍历顺序假设为相同,在这个假设的前提下,会做一些事情;
他们认为不应该对一个map中的元素的遍历顺序有假设,所以从Go1.0开始,随机化了range map的起始位置;
4.map并发:
1.多协程同时读和写就会出现 fatal error:concurrent map read and map write 的错误;
2.多个协程同时写也会出现 fatal error: concurrent map writes 的错误;
5.nil map (没有初始化的map)
a.range对于nil的map,默认按照空的map处理,也就是不运行for循环的逻辑代码;
b.对于nil的map,可以把它看成只读的map,除了写操作会panic,其它操作都可以正常运行;
注:nil的map 可用于参数读取,传入 nil 时不会报错;
c.nil的slice,除了不能索引外,其他的操作都是可以的,可以使用append函数,slice会自动进行扩充;
二.go语言的零值
1.在go语言中,任何类型在声明后没有赋值的情况下,都对应一个零值:
整形如int8、byte、int16、uint、uintprt等,默认值为0;
浮点类型如float32、float64,默认值为0;
布尔类型bool的默认值为false;
复数类型如complex64、complex128,默认值为0+0i;
字符串string的默认值为空字符串””;
错误类型error的默认值为nil;
对于一些复合类型,如指针、切片、字典、通道、接口,默认值为nil;
而数组的默认值要根据其数据类型来确定,例如:var a [4]int,其默认值为[0 0 0 0];
一个struct的零值意味着它的每个成员都是零值,因此struct初始化为零值是最理想的默认情况;
例如,对于bytes.Buffer,它的零值就是一个空缓存,sync.Mutex的零值是未使用的互斥锁;
2.结构体指针
a.指针表示指向内存的地址,如果对为nil的指针进行解引用的话就会导致panic;
b.指针的值为nil也可以调用指针对象的方法:
type person struct {}
func (p *person) sayHi() { fmt.Println("hi") }
var p *person
p.sayHi() // hi
作用:利用此特性可以减少一个不必要的 == nil 的判断;
3.interface
interface 并不是一个指针,它的底层实现由两部分组成,一个是类型,一个值,
也就是类似于:(Type, Value); 只有当类型和值都是nil的时候,才等于nil;
三.for range 中参数传递问题
错误代码:
for _, val := range values {
go func() {
fmt.Println(val)
}()
}
说明:所有协程收到的val参数可能都是切片的最后一个元素
正确写法:
for _, val := range values {
go func(val interface{}) {
fmt.Println(val)
}(val)
}
或者
for i := range valslice {
val := valslice[i]
go func() {
fmt.Println(val)
}()
}