Loading

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的基本操作

  1. 插入数据:m[k1]=v1
  2. 获取数据个数/容量:len, 不支持通过cap获取容量
  3. 查找和数据读取:v, ok := m["key"] //comma ok
  4. 删除数据:delete(map,k), 删除不存在的key不会导致panic,方法直接结束,不会产生显式提示
  5. 遍历数据: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的地址
posted @ 2024-02-07 17:18  brynchen  阅读(6)  评论(0编辑  收藏  举报