在C++
标准库中,std::unordered_map
的底层实现通常是基于哈希表,而哈希表的核心结构通常包含一个动态数组(类似于 std::vector
)来存储“桶”(buckets
)。每个桶内可能用链表或红黑树(C++11
后某些实现用)处理哈希冲突。以下是关键点:
1 数据结构
底层存储:哈希表需要一个连续的存储空间来管理桶,通常会使用动态数组(如 std::vector
的类似结构)来支持动态扩容。
冲突解决:每个桶内存储的是链表节点
或红黑树节点
(取决于实现)。
2 动态扩容机制
当哈希表的负载因子(元素数量 / 桶数量)超过阈值(默认为1.0
)时,会触发以下操作:
分配新数组:创建一个更大的动态数组(通常是原大小的两倍左右)。 重新哈希:将所有元素重新哈希到新的桶中。
释放旧数组:销毁旧桶数组。 这个过程与std::vector
的扩容逻辑类似,但需要重新哈希所有元素。
2.1 为什么用动态数组?
快速随机访问:通过哈希函数计算出的索引需要直接访问对应桶,动态数组的连续内存布局(O(1)
时间复杂度)满足这一需求。
内存效率:动态数组的内存分配比链表更紧凑,缓存局部性更好。
3 一些关键API
- 桶大小
std::unordered_map<int, std::string> myMap(100); // 初始桶数量为100
myMap.bucket_count(); // 获取当前桶的数量
- 重新哈希
如果你在插入大量元素后发现性能下降,可能是因为当前的桶数量不足以容纳所有元素,从而导致过多的冲突。在这种情况下,你可以通过重新哈希来增加桶的数量。这可以通过调用rehash函数来实现:
myMap.rehash(200); // 尝试将桶数量增加到200
- 负载因子
-
unordered_map
的负载因子是元素数量与桶数量的比率。当负载因子超过某个阈值(通常是1
),unordered_map
会自动进行重新哈希以增加桶的数量。 -
当元素减少使得负载因子降低时,
unordered_map
不会自动调整桶的数量,此时可以通过调用reserve
方法来预估所需的元素数量,这会触发一个合适的重新哈希:
myMap.reserve(1000); // 预计将有1000个元素,这可能导致重新哈希以增加桶的数量