[NOTE] Updated September 3, 2023. This article may have outdated content or subject matter.
golang 语言提供了 built-in 的 map 类型,提供 hash table 的功能。完成的特性主要有:增删查改。
定义初始化及使用注意
KeyType 必须是可比较的类型,可比较类型官方定义的是 boolean、numeric、string、pointer、channel、和 interface types、以及包含这些类型的 structs 或 arrays。不可比较的如 slices、maps 和 functions,用不了 ==
。
map 类型是引用类型,所以直接声明的变量0值是 nil,它并不指向一个初试化的 map,使用时要注意。
read nil, got zero. write nil, panic!
此外,由于 map 的 key 不存在时,默认为0值,利用这个特性也能做一些判断。
遍历 map 的顺序是随机的,如果要俺固定顺序遍历元素,可以考虑放入 slice 中。
多线程安全
map 在多线程情况下是不安全的,建议加读写锁 sync.RWMutex
一起使用。
底层实现
map = hashMap,本质是 array + list,效率极低:计算 hash 过程,查找过程
Hmap 两个 bucket 用于热扩容,不如 Java 8 的 Array + 红黑树高效,平均深度是 6.5
Hmap bucketsize 为 buckets 内 node 的数量,上限为 2 ^ B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
// A bucket for a Go map.
type bmap struct {
// tophash generally contains the top byte of the hash value
// for each key in this bucket. If tophash[0] < minTopHash,
// tophash[0] is a bucket evacuation state instead.
tophash [bucketCnt]uint8
// Followed by bucketCnt keys and then bucketCnt elems.
// NOTE: packing all the keys together and then all the elems together makes the
// code a bit more complicated than alternating key/elem/key/elem/... but it allows
// us to eliminate padding which would be needed for, e.g., map[int64]int8.
// Followed by an overflow pointer.
}
|
每个bucket中存放最多8个key/value对, 如果多于8个,那么会申请一个新的bucket,并将它与之前的bucket链起来。
将hash值的低位当作Hmap结构体中buckets数组的index,找到key所在的bucket。
将hash的高8位存储在了bucket的tophash中,先比对高 8 bit,再比对 key 是否相等。
内存布局如果是 key1/value1/key2/value2,设想如果是这样的一个 map[int64]int8
,考虑到字节对齐,会浪费很多存储空间。

参考资料
https://go.dev/blog/maps
https://github.com/golang/go/blob/master/src/runtime/map.go
Author
lawrshen
LastMod
2023-09-03
(3e79898)
License
CC BY-NC-ND 4.0