我正在创建自己的实现来为教育目的哈希表。
增加哈希表大小的最佳方法是什么?
我目前将哈希数组大小加倍。
我使用的散列函数是:key mod arraysize。
这个问题是如果键是:2,4,6,8,那么数组大小会不断增加。
克服这个问题的最佳方法是什么?有没有更好的方法来增加哈希表大小?改变我的散列函数会有帮助吗?
注意:我的密钥都是整数!
答案 0 :(得分:12)
哈希表通常通过确保哈希表大小是素数来避免此问题。调整表格大小时,将大小加倍,然后向上舍入到大于该值的第一个素数。这样做可以避免类似于您描述的群集问题。
现在,确实需要一点时间才能找到下一个素数,但不是很多。与重新散列哈希表内容所涉及的时间相比,找到下一个素数几乎不需要时间。有关说明,请参阅Optimizing the wrong thing。
答案 1 :(得分:3)
如果您尝试实现自己的哈希表,请参阅以下提示:
mod
作为哈希函数,则选择表格大小的素数。Quadratic Probing
查找碰撞的最终位置,h(x,i) = (Hash(x) + i*i) mod TableSize
碰撞i
。这是Quadratic Probing
的优雅工具:
//find a position to set the key
int findPos( int key, YourHashTable h )
{
int curPos;
int collisionNum = 0;
curPos = key % h.TableSize;
//while find a collision
while( h[curPos] != null && h[curPos] != key )
{
//f(i) = i*i = f(i-1) + 2*i -1
curPos += 2 * ++collisionNum - 1;
//do the mod only use - for efficiency
if( curPos >= h.TableSize )
curPos -= h.TableSize;
}
return curPos;
}
答案 2 :(得分:2)
OpenJDK使用2的幂来表示HashMap的容量,如果密钥都是2的幂的倍数,则会导致很多冲突。它通过在键的hashCode:
之上应用另一个哈希函数来防止这种情况/**
* Applies a supplemental hash function to a given hashCode, which defends against poor quality hash functions.
* This is critical because HashMap uses power-of-two length hash tables, that otherwise encounter collisions
* for hashCodes that do not differ in lower bits. Note: Null keys always map to hash 0, thus index 0.
*/
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
答案 3 :(得分:1)
哈希和哈希函数是一个复杂的主题,幸运的是有很多在线资源。
目前尚不清楚如何确定阵列大小。
在Java HashMap
实现中,底层数组的大小始终是2的幂。这有一点点优势,您不需要计算模数,但可以计算数组索引as index = hashValue & (array.length-1)
(相当于array.length
是2的幂时的模运算)。
此外,HashMap
使用了一些"魔术功能"减少哈希冲突的数量,以防多个哈希值仅以常数因子相差的情况,如示例所示。
然后,通过"加载因子"确定数组的实际大小。 (您甚至可以将其指定为HashMap
的构造函数参数)。当占用的数组条目数超过loadFactor * array.length
时,数组的长度将加倍。
这个负载因子允许一定的权衡:当负载因子很高(0.9左右)时,更可能发生哈希冲突。当它很低(0.3左右)时,哈希冲突将不太可能,但会有很多浪费"空格,因为在任何时间点实际上只会占用数组的少数条目。