目录
Please enable Javascript to view the contents

Go Map 详解——底层结构与使用注意事项

 ·  ☕ 2 分钟

重要

Go 的 map 是引用类型,函数传参时修改会影响原始 map。Map 不是并发安全的——并发读写会触发 fatal error(不可 recover)。需要并发安全时用 sync.MapMutex + map

1. 基础操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 创建
m := make(map[string]int)        // 空 map
m := make(map[string]int, 100)   // 建议预分配容量
m := map[string]int{"a": 1}       // 字面量初始化

// 读写
m["key"] = 1
v := m["key"]
v, ok := m["key"]  // ok=false 表示 key 不存在

// 删除
delete(m, "key")

// 遍历(顺序不确定)
for k, v := range m { }

2. Map 底层结构

Go map 使用哈希表实现,通过链地址法解决哈希冲突。每个 bucket 最多存储 8 个键值对。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
hmap
├── count         # 元素数量
├── B             # 2^B 个 bucket
├── buckets       # bucket 数组
└── oldbuckets    # 扩容时指向旧 bucket

bmap
├── tophash[8]    # 每个 key 的 hash 高 8 位
├── keys[8]       # 8 个 key
├── values[8]     # 8 个 value
└── overflow      # 溢出 bucket 指针

查找 key 时:

  1. 计算 hash
  2. 用 hash 低位定位 bucket
  3. 用 hash 高 8 位(tophash)快速对比
  4. tophash 匹配后再比较完整 key

3. 扩容机制

触发条件类型操作
负载因子 > 6.5(元素数 > bucket 数 × 6.5)增量扩容创建 2 倍新 bucket,逐步迁移
overflow bucket 过多等量扩容重新整理,合并碎片

扩容是渐进式的——每次 map 操作(读写删除)会迁移 1-2 个旧 bucket,避免一次性 STW。

4. 并发安全

Map 并发读写会 fatal error(非 panic,不可 recover):

1
2
3
// fatal error: concurrent map writes
go func() { m["a"] = 1 }()
go func() { m["b"] = 2 }()

并发安全方案:

方案适用场景
sync.Mutex + map写多读少
sync.RWMutex + map读多写少
sync.Map读多写少、key 集合稳定

sync.Map 内部原理

sync.Map 使用两个 map:read(只读,原子访问)和 dirty(写时复制)。读操作优先查 read,无锁;写操作加锁操作 dirty。当 read 中 miss 太多时,dirty 提升为 read

1
2
3
4
5
var m sync.Map
m.Store("key", "value")
v, ok := m.Load("key")
m.Range(func(k, v interface{}) bool { return true })
m.Delete("key")

5. Map 作为函数参数

Map 是引用类型,传递给函数的是指针的副本,仍指向同一底层数据结构。

1
2
3
func modify(m map[string]int) {
    m["a"] = 100  // 会影响调用者的 map
}
类型传参行为
map引用传递——函数内修改影响外部
slice引用传递——修改元素影响外部;但扩容分配新底层数组后,外部不受影响
array值传递——完整拷贝

6. 总结

  1. Map 是引用类型,传参时修改影响原 map,但非并发安全;
  2. 扩容是渐进的——每次操作迁移 1-2 个 bucket,避免一次性 STW;
  3. 预分配容量 make(map[K]V, N) 减少扩容次数;
  4. 并发安全选型:读多写少用 sync.Map,否则用 Mutex + map

7. 参考

分享

Hex
作者
Hex
CloudNative Developer