Go map底层原理

一、map底层结构

链地址法

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
}

img
二、扩容
1. 扩容条件
  • 当溢出桶超过限制后,进行等量扩容,目的是整理溢出桶,提高检索效率

    if overflow < 2^15 :
    	overflow 桶数量大于2^B进行等量扩容
    if overflow > 2^15
        overflow 桶数量大于2^15就进行等量扩容
    
  • 装载因子6.5, 当存储的元素超过后进行增量扩容,扩容一倍

    元素总数 < 2^B * 6.5
    
2. 扩容时机
  • 扩容过程是渐进性的,主要是防止一次扩容需要搬迁的 key 数量过多,引发性能问题。

  • 触发扩容的时机是增加了新元素, 桶搬迁的时机则发生在赋值、删除期间,每次最多搬迁两个桶。

三、查找,删除,修改过程
  1. 先通过hash函数计算出key的哈希值—— ℎ𝑎𝑠ℎ。
  2. 在根据哈希值,计算出key所在桶的位值—— ℎ𝑎𝑠ℎ & (2𝐵−1)
  3. 遍历桶(bucket)中所有元素,对比tophash数组中i位置的值和本次要插入的key的tophash(hash的高8位)是否相等,如果tophash相等,还要进一步比较对应位置的key的值是否相等;相等则修改对应位置的value。否则找到空位,然后讲新的值插入。
  4. 如果桶中8个元素全部装满了,则需要将元素放到溢出桶中,没有溢出桶,则需要新建溢出桶,新建溢出桶的逻辑为优先使用初始化时创建的预分配溢出桶,如果预分配溢出桶耗尽,再新建。
四、遍历
  • map 遍历的核心在于理解 2 倍扩容时,老 bucket 会分裂到 2 个新 bucket 中去。而遍历操作,会按照新 bucket 的序号顺序进行,碰到老 bucket 未搬迁的情况时,要在老 bucket 中找到将来要搬迁到新 bucket 来的 key
  • 遍历过程中的时候触发扩容,不受影响,因为已经开始遍历的通不会受这个通是old还是正常情况的影响。
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值