【问题标题】:Best way to resize a hash table调整哈希表大小的最佳方法
【发布时间】:2014-04-21 15:47:09
【问题描述】:

我正在创建自己的实现来散列表以用于教育目的。

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

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

我使用的哈希函数是:key mod arraysize。

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

解决此问题的最佳方法是什么?有没有更好的方法来增加哈希表的大小?更改我的哈希函数会有帮助吗?

注意:我的键都是整数!

【问题讨论】:

  • 您正在编写自己的实现吗?为什么?最好的方法是从不调整大小。
  • 是的。有时需要调整大小,因为您不知道将添加多少元素。我正在制作自己的实现,因为它适用于我在大学的 CS 课程。
  • 没有“最好”的方法。这总是需要妥协。
  • (但正如其他人所说,您需要正确散列您的密钥。)
  • @HotLicks 寻找一种永远不需要调整(复制)底层数组/结构的方法/实现是最好的方法。如果有人设计出一种无需复制数据即可扩展底层结构的方法,那么新 Map 将是近乎完美的数据存储解决方案

标签: data-structures hashmap


【解决方案1】:

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);
 }

【讨论】:

  • 我的键都是整数,所以这可能不是最好的方法
  • @YahyaUddin 如果键是整数,应该没有任何区别。它们仍然可以以相同的方式重新散列。
【解决方案2】:

散列和散列函数是一个复杂的话题,幸运的是有很多在线资源。

目前尚不清楚您如何确定数组大小。

在 Java HashMap 实现中,底层数组的大小始终是 2 的幂。这有一点好处,您不需要计算模数,但可以将数组索引计算为 index = hashValue & (array.length-1) (这相当于array.length 是 2 的幂时的模运算)。

此外,HashMap 使用一些“魔术函数”来减少哈希冲突的数量,以防多个哈希值仅相差一个常数因子,如您的示例所示。

然后数组的实际大小由“负载因子”确定。 (您甚至可以将其指定为HashMap 的构造函数参数)。当被占用的数组条目数超过loadFactor * array.length时,则数组长度会加倍。

这个负载因子允许一定的权衡:当负载因子很高(0.9 左右)时,更可能发生哈希冲突。当它较低(0.3 左右)时,哈希冲突将不太可能发生,但会有很多“浪费”的空间,因为在任何时间点实际上只有很少的数组条目会被占用。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    【解决方案4】:

    如果您尝试实现自己的哈希表,这里有一些提示:

    1. 如果您使用mod 作为哈希函数,请选择一个素数作为表大小。
    2. 使用Quadratic Probing 查找碰撞的最终位置,h(x,i) = (Hash(x) + i*i) mod TableSize 查找ith 碰撞。
    3. 当哈希表半满时,将大小加倍到最接近的素数,如果您的碰撞函数适合您的输入,您将永远不会这样做。

    这是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;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-10-14
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 2013-12-21
      • 2017-06-07
      • 2021-03-14
      • 1970-01-01
      相关资源
      最近更新 更多