go 的 map 实现原理

2022-04-25  本文已影响0人  wayyyy

Go 语言的map 使用 hash 表作为底层实现,一个 Hash 表可以有多个 bucket,而每个 bucket 保存了 map 中的一个或一组键值对。

map的内存模型
map.jpg
type hmap struct {
    count      i nt               // 元素个数
    flags      uint8
    B          uint8             // buckets 数组的长度就是 2^B 
    hash0      uint32 
    buckets    unsafe.Pointer    // 指向 buckets 数组,大小为 2^B,如果元素个数为0,就为 nil
    oldbuckets unsafe.Pointer    // 老旧的 buckets 数据,用来扩容 
    nevacuate  uintptr       
    extra      *mapextra 
}

buckets 是一个指针,最终它指向的是一个结构体:

type bmap struct {
    tophash [bucketCnt]uint8
}

但这只是表面(src/runtime/hashmap.go)的结构,编译期间会给它加料,动态地创建一个新的结构:

type bmap struct {
    topbits  [8]uint8
    keys     [8]keytype
    values   [8]valuetype
    pad      uintptr
    overflow uintptr    // 溢出buckets
}

bmap 就是我们常说的 bucket,元素经过 hash 运算后就会落到某个 bucket 中存储。

image.png

注意到 key-value 不是 ke/value key/value 这样放在一起的,是 key/key/.../value/value/的形式,源码解释是为了可以省掉paddding,节省内存空间。

hash 冲突
扩容
key 的查找

查找过程如下:

如果当前 map 处于搬迁过程中,那么查找时优先从 oldbuckets 数组中查找。然后再从新的buckets 数组中查找。

key 的添加

新元素添加过程如下:

如果当前 map 处于搬迁过程中,那么新元素会直接添加到新的buckets 数组中。

key 的删除

删除元素实际上先查找元素,如果元素存在则把元素从相应的 bucket 中清除,如果不存在则什么都不做。

上一篇下一篇

猜你喜欢

热点阅读