Go语言中的map是通过哈希表实现的,其底层结构和实现机制如下:
一、hash 结构
hmap结构体:是map的头部结构,主要字段及含义如下:
- count:表示当前哈希表中的元素数量,与len()函数相对应。
- flags:标记字段,用于标记是否正在进行读写操作,以便实现并发读写的检测。 所以它不是并发安全的
- B:表示当前哈希表持有的buckets数量的对数,即len(buckets) == 2^B。
- noverflow:溢出桶的大致数量。
- hash0:hash种子。
- buckets:存储2^B个桶的数组,是一个unsafe.Pointer,因为Go语言中支持不同类型的键值对,需要在编译时才能确定map的类型。
- oldbuckets:扩容时用于保存之前的buckets的字段,大小是buckets的一半。
- nevacuate:迁移进度计数器,记录buckets中小于该值的bucket已经完成迁移。
- extra:指向mapextra结构体的指针,用于存储一些可选字段。
type hmap struct {
// 元素个数,调用 len(map) 时,直接返回此值
count int
flags uint8
// buckets 的对数 log_2
B uint8
// overflow 的 bucket 近似数
noverflow uint16
// 计算 key 的哈希的时候会传入哈希函数
hash0 uint32
// 指向 buckets 数组,大小为 2^B
// 如果元素个数为 0,就为 nil
buckets unsafe.Pointer
// 扩容的时候,buckets 长度会是 oldbuckets 的两倍
oldbuckets unsafe.Pointer
// 指示扩容进度,小于此地址的 buckets 完成迁移
nevacuate uintptr
extra *mapextra
}
bmap结构体:是哈希表中的桶,每个bmap能够存储8个键值对,并且设有一个指针,当某个bmap存满时,就会申请新的bmap进行存储,并与前一个bmap构成链表。其结构如下:
- tophash:数组,用于存储每个key hash之后的高位hash值。
- keys:数组,用于存储key。
- elems:数组,用于存储value。
- overflow:溢出指针,指向下一个bmap的地址。
下面是map 的初始形态,
type bmap struct {
tophash [bucketCnt]uint8
}
但是编译器会对go 的map 给塞几个字段
type bmap struct {
topbits [8]uint8
keys [8]keytype
values [8]valuetype
pad uintpt