1、本文讨论Golang的哈希表
Golang哈希表的实现,底层数据结构是数组+单链表,链表节点由8个key、value和键的高八位组成的。为了方便理解,先简单看一个图快速理解。
我们来看一下Golang哈希表的结构体定义
简单介绍一下结构体中几个关键的字段,hmap结构体就是Golang哈希表的底层结构体。
buckets为 哈希表 底层实际存储哈希表数据(数组+单链表)的指针变量。
oldbuckets,也是存储哈希表数据(数组+单链表)的指针变量。但是,这个oldbuckets是用来存储 旧数据 的,用于在哈希表扩容时,渐进式扩容使用的,渐进式扩容结束时,oldbuckets指向的数据会被删除。后面我们再说扩容的事情。
count 表示当前哈希表中的元素数量,有一个很重要的意义就是,可以通过count知道渐进式扩容什么时候结束。
hash0是哈希值的随机种子,这些都不是很重要。
extra是存储溢出桶的,溢出桶其实也是数组+单链表,可以简单理解成它的目的,是为了降低扩容频率而产生的
什么是桶,笔者理解其实就是那个底层数组。。。
介绍完关键几个字段,我们再看一张图便于理解
可以看到buckets指向的是一个数组,那么数组元素bmap就是一个“单链表”,所以才说Golang哈希表是数组+单链表,我们来看看bmap的数据结构
链表节点由8个key、value和键的高八位组成的,overflow是指向下一个节点的指针,topbits就是8个键的高八位,作用是为了加速查找。
其实到这里,你已经明白了哈希表整体的结构,那么我们来说说Golang哈希表是什么时候触发扩容?扩容行为是什么
2、什么时候触发扩容
(1)溢出桶过多,也就是底层数组过长的时候
(2)负载因子达到某个阈值的时候
为什么溢出桶过多,也会触发扩容行为?
因为当溢出桶过多,但是桶中数据很少的时候。因为这时候键值对比较分散,查找的性能比较差,需要将键值对聚集一下
3、扩容行为有什么
(1)等量扩容
等量扩容,就是溢出桶过多,也就是底层数组过长的时候触发的,只是把键值对存储的更密集一些
(2)翻倍扩容
创建两倍的桶数量(两倍数组长度),然后将旧数据以渐进式扩容的方式迁移旧数据,新数据直接写到新的桶中,我们看一个图方便理解,什么时候结束扩容?我们之前说了一个count字段,我们记录一下迁移了多少个,就知道是否完全迁移结束了
什么是渐进式扩容
①就是不一次性把数据复制过去,如果数据量多大,会短时间消耗大量性能
②首先把扩容前的桶标记为旧桶,
----1)查找操作,先看对应桶是否已经迁移,没有迁移就查旧桶,然后把对应桶数据迁移过去,迁移次数-1,如果迁移次数为0,就表示整个哈希表完成了迁移
更新、删除操作类似,先看是否完成迁移,没有迁移,先在旧桶完成迁移,再到新桶进行相应操作。
写文不易,给个点赞关注吧哈哈