持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
文章概览
- 实现原理
- 扩容
一、Map实现原理
Go语言使用Hash来实现Map,其中,一个Hash里可以有多个bucket,而每个bucket保存了map中的一个或多个键值对。
map的数据结构由hmapstruct定义,其中包含当前保存的元素个数count、bucket数组的大小B、指向bucket数组的指针buckets、指向旧bucket数组的指针。
bucket特性
- bucket最多可以存储8个键值对。
- tophash是一个长度为8的数组,存储hash值的高8位。
- data区存放的是key-value数据。
- overflow指针指向下一个bucket,将所有冲突的键连接起来。
Hash冲突
定义: 当两个或以上的键分配到同一个bucket时,称这些键发生了冲突。当bucket放满时就会创建新的bucket,并用类似链表的方法连接起来。
问题: bucket中使用指针指向溢出的bucket,但是Hash冲突降低了存取效率。
负载因子
定义: 负载因子即用来衡量Hash表冲突的指标。负载因子 = 键数量/bucket数量。
- 负载因子过大:冲突严重,存取效率低。
- 负载因子过小:空间利用率低。
负载因子随着map中的元素数量的增加而升高,当负载因子过大时,会申请更多bucket,并会使所有键值对重新组织,从而使元素在bucket中分布更均匀,这个过程也成为rehash。
二、扩容
扩容条件
通常为了降低负载因子,保证效率,每添加一次新元素就会检查是否需要扩容。
- 当负载因子大于6.5时,会触发扩容。
- 当overflow的数量过高时,会发生扩容。
扩容过程
首先hmap中的oldbuckets指针指向原来的buckets数组,然后申请长度为原来两倍的新的buckets数组,并使buckets指针指向它,最后进行数据迁移,oldbuckets数组中所有的键值对都复制到新buckets数组时,就会释放olebuckets数组。
删除元素
删除元素首先根据key查找值,如果元素存在则将元素从相应的bucket中清除,如果不存在则什么也不做。