Golang Map 底层实现

Golang Map 底层概述

Golang 中 map 的底层实现是一个散列表,因此实现 map 的过程实际上就是实现散表的过程。在这个散列表中,主要出现的结构体有两个,一个叫 hmap(a header for a go map),一个叫 bmap(a bucket for a Go map,通常叫其bucket)。

在这里插入图片描述

Golang Map 查找

Go 语言中 map 采用的是哈希查找表,由一个 key 通过哈希函数得到哈希值,64位系统中就生成一个 64bit 的哈希值,由这个哈希值将 key 对应到不同的桶 (bucket)中,当有多个哈希映射到相同的的桶中时,使用链表解决哈希冲突。key 经过 hash 后共 64 位,根据 hmap 中 B 的值,计算它到底要落在哪个桶 时,桶的数量为 2^B,如 B=5,那么用 64 位最后 5 位表示第几号桶,在用 hash值的高 8 位确定在 bucket 中的存储位置,当前 bmap 中的 bucket 未找到,则查询对应的 overflow bucket,对应位置有数据则对比完整的哈希值,确定是否 是要查找的数据。

Golang Map 底层

负载因子

存储的键值对的数目与桶的数目的比值称为负载因子,默认为6.5。

渐进式扩容

如果哈希表存储的键值对较多,一次性迁移所有桶花费的时间就比较久,所以通常在哈希表扩容时,先分配足够多的新桶,然后用一个字段记录旧桶的位置,一个字段记录旧桶迁移的进度,在哈希表每次读写操作时,如果检测到当前处于扩容阶段,就完成一部分键值对迁移任务,直接所有旧桶迁移完成,旧桶不再使用,才算真正完成一次哈希表的扩容,像这样把键值对迁移的时间分摊到多次哈希表操作中的方式,就是渐进式扩容,可以避免一次性扩容带来的性能瞬时抖动。

hmap

在这里插入图片描述Go语言中map类型的底层实现就是哈希表,map类型的变量本质是是一个指针,指向hmap结构体。

cout记录键值对的数目
B记录桶的数目是2的多少次幂
noverflow记录使用的溢出桶的数量
buckets记录桶在哪
oldbuckets用于扩容阶段记录旧桶在哪
nevacuate记录渐进式扩容阶段下一个要要迁移的旧桶编号

bmap

在这里插入图片描述map使用的桶就是bmap,一个桶里可以放8个键值对,但是为了让内存排列更加紧凑,8个key放一起,8个value放一起,8个key的前面则是8个tophash,每个tophash都是对应哈希值的高8位。最后这里是一个bmap型指针,指向一个溢出桶overflow,溢出桶的内存布局与常规桶相同,是为了减少扩容次数而引入的,当一个桶存满了,还有空用的溢出桶时,就会在桶后面链一个溢出桶,继续往这里面存。
在这里插入图片描述实际上如果哈希表要分配的桶的数目大于2的4次(16)就认为使用到溢出桶的几率较大,就会预分配2的(B-4)个溢出桶备用,这些溢出桶与常规同在内存中是连续的,只是前面2的B次个用做常规桶, 后面的用做溢出桶。

hmap结构体最后有一个extra字段,指向一个mapextra结构体,里面记录的都是溢出桶相关的信息

overflow是一个slice,记录目前已经使用的溢出桶的地址
oldoverfolw也是一个slice,用于在扩容阶段存储旧桶用到的那些溢出桶的地址
nextoverflow指向下一个空闲溢出桶

扩容规则

在这里插入图片描述

翻倍扩容

在这里插入图片描述Go语言中map的默认负载因子是6.5,超过这个数就会触发翻倍扩容。
在这里插入图片描述

等量扩容

如果负载因子没有超标,但是使用的溢出桶很多,也会触发扩容。不过这一次是等量扩容。
如果常规桶数目不大于2^15,那么使用溢出桶数目超过常规同就算是多了。
如果常规桶数目大于2 ^ 15,那么使用溢出桶数目一旦超过2^15就算是多了。
所谓等量扩容,就是创建和旧桶数目一样多的新桶,然后把原来的键值对迁移到新桶中,但是既然等量,迁移来迁移去有什么用?那我们就要想想什么情况下,桶的负载因子没有超过上限值,却偏偏使用了很多溢出桶呢?自然是由很多键值对被删除的情况, 就像这里编号为0的情况。
在这里插入图片描述
如果此时满足等量扩容的触发条件,就会分配等量的新桶,编号为0的旧桶依然会迁移到同样编号的新桶中,同样数目的键值对迁移到新桶中,能够排列的更加紧凑,从而减少溢出桶的使用,这就是等量扩容的意义所在。

参考视频:链接

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值