总结一下 Go map
的哈希冲突解决机制。
1. 哈希表结构:
Go 语言的 map
底层有两个主要结构:hmap
和 bmap
,它们分别负责管理整个 map
的元数据和存储键值对的桶。
-
hmap:包含
map
的元数据,如桶的数量、已插入的键值对数量、哈希种子等。 -
bmap:是实际存储数据的桶,多个键值对存储在一个桶中。每个桶最多存储 8 个键值对。
2. 哈希冲突与链地址法:
当发生哈希冲突时,Go 通过 链地址法 解决冲突,但与传统的链地址法(在每个桶内存储链表)不同,Go 使用了溢出桶(overflow bucket)的机制来避免在桶内使用链表存储。
-
当某个桶已经存储满 8 个键值对时,如果再有新的键值对要插入,就会创建一个新的溢出桶,并把这个溢出桶链接到当前桶的后面。这种方式是通过“桶链”来存储冲突的键值对。
-
每个溢出桶也有相同的存储结构,这样就形成了一个桶和溢出桶的链表。
3. 存储方式:
Go map
的内存布局非常高效。每个桶存储的 8 个键值对是 连续 存储的。为了提高查找速度,还会将每个键值对的哈希值的 高 8 位 存储在桶内,辅助进行快速查找。
-
每个桶存储的 8 个键是连续存储的,之后是 8 个值。
-
另外,还有一个字节数组用于存储每个键的哈希值的高 8 位,这样可以加快哈希查找的速度。
4. 内存与性能优化:
这种设计让 Go 的 map
在插入和查找时,能够在大多数情况下提供非常好的性能。由于每个桶里的数据是连续存储的,它避免了传统链地址法中使用链表可能带来的指针跳转和内存碎片问题。因此,Go 的 map
在内存访问上有很大的优势,尤其是在大量键值对的场景下。
总结:
-
链地址法:Go 通过桶和溢出桶链表的设计来解决哈希冲突。
-
高效存储:每个桶最多存 8 个键值对,且键值对是连续存储的,减少了内存碎片。
-
优化哈希查找:存储了键的高 8 位哈希值,加速了查找过程。