go-map原理解析


| 阅读 |,阅读约 2 分钟
| 复制链接:

Overview

map原理

  • map数据结构
  • map初始化
  • 插入
  • 删除
  • 扩容
  • 遍历
  • NAN

结构体

源码位置:src/runtime/map.go

 1type hmap struct {
 2  // 元素数量
 3  count     int
 4  // 标识目前map的状态:写、扩容
 5  flags     uint8
 6  // log_2 桶数量
 7  B         uint8
 8  // 元素超出该数量认为溢出
 9  noverflow uint16
10  hash0     uint32 // hash seed
11  // 桶数组,指向bmap,长度为 2^B
12  buckets    unsafe.Pointer
13  // 扩容用的桶
14  oldbuckets unsafe.Pointer
15  // 扩容进度
16  nevacuate  uintptr
17  // 可选字段,额外属性
18  extra *mapextra // optional fields
19}
20
21// 桶
22type bmap struct {
23  // 不仅仅只有这个字段,其他字段是在编译时加进去的,因为map中存放的数据类型不确定
24  // 参考 src/cmd/compile/internal/gc/reflect.go的bmap函数
25
26  // bucketCnt=8,也就是bmap中的桶固定只有8个格子,每个格子内存放一对key-value,
27  // 表示key-value的字段并不在这里表示,而是编译时动态添加的
28  tophash [bucketCnt]uint8
29
30  // 其他编译生成的字段包括
31  // overflow 用于连接下一个bmap(溢出桶)
32}

map初始化

 1func makemap(t *maptype, hint int, h *hmap) *hmap {
 2  mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)
 3  if overflow || mem > maxAlloc {
 4    hint = 0
 5  }
 6
 7  // initialize Hmap
 8  if h == nil {
 9    h = new(hmap)
10  }
11  h.hash0 = fastrand()
12
13  // Find the size parameter B which will hold the requested # of elements.
14  // For hint < 0 overLoadFactor returns false since hint < bucketCnt.
15  B := uint8(0)
16  for overLoadFactor(hint, B) {
17    B++
18  }
19  h.B = B
20
21  // allocate initial hash table
22  // if B == 0, the buckets field is allocated lazily later (in mapassign)
23  // If hint is large zeroing this memory could take a while.
24  if h.B != 0 {
25    var nextOverflow *bmap
26    h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)
27    if nextOverflow != nil {
28      h.extra = new(mapextra)
29      h.extra.nextOverflow = nextOverflow
30    }
31  }
32
33  return h
34}