Go语言精进之路读书笔记第14条——了解map实现原理并高效使用
14.1 什么是map
map对value的类型没有限制,但是对key的类型有严格要求:key的类型应该严格定义了作为“==”和“!=”两个操作符的操作数时的行为,因此func、map、slice、chan不能作为map的key类型。
map类型不支持“零值可用”,未显式赋初值的map类型变量的零值为nil。对处于零值状态的map变量进行写操作将会导致运行时panic,进行读操作会返回 val 类型的零值作为兜底。
在创建map时,尽量使用cap参数
- 使用复合字面值创建map类型变量
- 使用make创建map类型变量L:
make(map[string]int, cap)
14.2 map的基本操作
- 插入数据:
m[k1]=v1
- 获取数据个数/容量:
len
, 不支持通过cap获取容量 - 查找和数据读取:
v, ok := m["key"] //comma ok
- 删除数据:
delete(map,k)
, 删除不存在的key不会导致panic,方法直接结束,不会产生显式提示 - 遍历数据:
for k,v := range m //遍历是随机的
遍历数据为什么是随机的?
map源码中mapiterinit函数,Go语言为了防止依赖map的循环顺序,故意在key的hash值上又加上了一个随机值
// $GOROOT/src/runtime/map.go:685
// mapiterinit initializes the hiter struct used for ranging over maps.
// The hiter struct pointed to by 'it' is allocated on the stack
// by the compilers order pass or on the heap by reflect_mapiterinit.
// Both need to have zeroed hiter since the struct contains pointers.
func mapiterinit(t *maptype, h *hmap, it *hiter) {
...
// decide where to start
r := uintptr(fastrand())
if h.B > 31-bucketCntBits {
r += uintptr(fastrand()) << 31
}
...
}
14.3 map的内部实现
//todo
14.4 尽量使用cap创建map
特点
- map和切片一样,也是引用类型,作为函数参数不会有很大性能损耗
- 不要依赖map的元素遍历顺序
- map不支持并发读写,只支持并发读,如果要并发读写,可以采用sync.Map
- 由于map可以自动扩容,map中数据元素的value位置可能在这一过程中发生变化,因此Go不允许获取map中value的地址