调整哈希表大小的最佳方法

时间:2014-03-16 13:24:06

标签: data-structures hashmap

我正在创建自己的实现来为教育目的哈希表。

增加哈希表大小的最佳方法是什么?

我目前将哈希数组大小加倍。

我使用的散列函数是:key mod arraysize。

这个问题是如果键是:2,4,6,8,那么数组大小会不断增加。

克服这个问题的最佳方法是什么?有没有更好的方法来增加哈希表大小?改变我的散列函数会有帮助吗?

注意:我的密钥都是整数!

4 个答案:

答案 0 :(得分:12)

哈希表通常通过确保哈希表大小是素数来避免此问题。调整表格大小时,将大小加倍,然后向上舍入到大于该值的第一个素数。这样做可以避免类似于您描述的群集问题。

现在,确实需要一点时间才能找到下一个素数,但不是很多。与重新散列哈希表内容所涉及的时间相比,找到下一个素数几乎不需要时间。有关说明,请参阅Optimizing the wrong thing

答案 1 :(得分:3)

如果您尝试实现自己的哈希表,请参阅以下提示:

  1. 如果您使用mod作为哈希函数,则选择表格大小的素数。
  2. 使用Quadratic Probing查找碰撞的最终位置,h(x,i) = (Hash(x) + i*i) mod TableSize碰撞i
  3. 当哈希表达到半满时,将大小加倍到最接近的素数,如果你的碰撞功能对你的输入没问题,你将永远不会做。
  4. 这是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左右)时,哈希冲突将不太可能,但会有很多浪费"空格,因为在任何时间点实际上只会占用数组的少数条目。