一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
3. map
推荐这篇博文 Map底层实现
3.1 底层结构
type hmap struct {
// Make sure this stays in sync with the compiler's definition.
count int // 键值对的数量
flags uint8
// 状态标识,比如正在被写、buckets和oldbuckets在被遍历、等量扩容(Map扩容相关字段)
B uint8 // 2^B=len(buckets)
noverflow uint16 // 溢出桶里bmap大致的数量
hash0 uint32 // hash 因子
buckets unsafe.Pointer
// 指向一个数组(连续内存空间),数组的类型为[]bmap,这个字段我们可以称之为正常桶
oldbuckets unsafe.Pointer
// 扩容时,存放之前的buckets(Map扩容相关字段)
nevacuate uintptr
// 分流次数,成倍扩容分流操作计数的字段(Map扩容相关字段)
extra *mapextra
// 溢出桶结构,正常桶里面某个bmap存满了,会使用这里面的内存空间存放键值对
}
map的读是无序的,如果需要顺序读取,可以将这个key进行排序,然后再按照这个key打印输出
3.2 赋值
var mp1 map[int][int] // 1
mp1[1]=1
mp2 := map[int][int]{} // 2
mp2[1]=1
情况 1 :报错,var的赋值 mp1是为nil的。 情况 2 :可行,而第二种情况的赋值是不为nil的。
3.3 map 是线程安全的吗?
map的底层hmap是没有加锁的。所以并不是线程安全的。如果需要线程安全的map,我们可以使用官方定义的sync下的sync.Map。
3.4 map的扩容注意点
我们可以看出正常桶的bmap和溢出桶的bmap实际构成了链表关系,所以Go里面Map的实现主要用到了数组,其次还用到了链表。
以下写法是错误的,因为当map扩容的时候,他的扩容地址会发生变化,所以key value都是不可取地址。并且map删除key之后是不会自动缩容的。
m:=make(map[int][int])
m[1]=1
fmt.Println(&m[1])