我试图了解 STL 无序集合/映射(即哈希映射)如何工作。
我知道初始哈希表大小(即桶的数量)设置为 8,当更多元素添加到集合/映射中时,这个数字将一次又一次地翻倍。
因此,AFAIU set/map 应该保留一个存储桶数组。
我知道使用的默认哈希函数(例如,对于 int)是:
size_t std::hash<int>(int key);
根据定义,哈希函数将键映射到哈希表中的索引,即映射到桶数组中桶的索引。
因此,哈希函数需要将任何 int 键映射到从 0 到存储桶数组大小的索引。 但是,哈希函数不会接收桶数组的大小,那么它是如何知道的呢?
如果我提供自己的哈希函数,该函数将仅接收密钥,则相同,例如:
struct HashFunctionCalc
{
size_t operator()(int key) const { return ... }
};
在不知道有多少个桶的情况下,它如何知道将这个键映射到桶的索引?
提前致谢...
您对 std::unordered_set 和 std::unordered_map 使用哈希表进行快速平均情况查找的核心概念是正确的。以下是散列在这些容器中的工作原理:
哈希函数和桶:
哈希函数:您已经正确识别了哈希函数的作用。它需要一个键并将其映射到一个数值(存储桶索引)。然而,哈希函数本身并不能直接知道哈希表中的桶数。
存储桶大小计算:在内部,std::unordered_set 和 std::unordered_map 维护存储桶数量的 2 的幂值。该值通常通过按位运算获得(例如 1 << x) where x is an internal variable tracking the number of resize operations. It ensures the number of buckets is always a power of two for efficient modulo operation (explained later).
将键映射到存储桶:
哈希函数输出:您提供的哈希函数(默认或自定义)返回一个 size_t 值,该值是无符号整数类型。
取模运算:然后将此 size_t 值与实际桶数 (num_buckets) 进行取模运算 (%)。此模运算可确保结果索引落在存储桶数组的有效范围内(0 到 num_buckets - 1)。
示例:
假设当前的桶数 (num_buckets) 是 8(2 的 3 次方)。 整数键的哈希函数返回值 23。 执行 23 % 8 会给你 3。 因此,哈希值为 23 的键将被放置在内部桶数组的索引 3 的桶中。
自定义哈希函数:
您的自定义哈希函数(HashFunctionCalc)只需要担心为密钥生成一个好的哈希值。它不需要知道桶的数量。 std::unordered_set 或 std::unordered_map 将执行模运算以确保结果索引在有效范围内。 总结:
哈希函数专注于在存储桶之间创建良好的密钥分布。 容器本身管理桶的数量并执行取模运算以适应桶数组中的哈希值。