java - 为什么 HashMap 会重新哈希键对象提供的哈希码?

标签 java collections hashmap hash hashcode

我正在阅读 Java 1.6 API 提供的 HashMap 类的代码,无法完全理解以下操作的需要(在 put 和 get 方法的主体中找到):

int hash = hash(key.hashCode());

hash() 方法的主体如下:

 private static int hash(int h) {
         h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

这通过对提供的哈希码执行位操作有效地重新计算哈希。我无法理解这样做的必要性,即使 API 声明如下:

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.

我确实理解键值解析存储在数据结构数组中,并且该数组中项目的索引位置由其哈希确定。 我不明白的是这个函数如何为散列分布添加任何值。

最佳答案

正如 Helper 所写,它的存在只是为了防止现有的关键对象哈希函数出现故障,并且不能很好地混合低位。根据the source由 pgras 引用,

 /**
  * Returns index for hash code h.
  */
 static int indexFor(int h, int length) {
     return h & (length-1);
 }

散列以 2 的幂长度进行 AND 运算(因此,length-1 保证是 1 的序列)。由于这个 ANDing,只有 h 的低位被使用。 h 的其余部分将被忽略。想象一下,无论出于何种原因,原始哈希仅返回可被 2 整除的数字。如果直接使用它,则永远不会使用 hashmap 的奇数位置,从而导致冲突次数增加 x2。在真正病态的情况下,糟糕的哈希函数会使 HashMap 表现得更像一个列表,而不是一个 O(1) 容器。

Sun 工程师必须进行测试,证明太多的散列函数在其低位不够随机,而且许多散列映射不够大,无法使用高位。在这些情况下,HashMap 的 hash(int h) 中的位操作可以提供对大多数预期用例的净改进(由于较低的冲突率),即使需要额外的计算也是如此。

关于java - 为什么 HashMap 会重新哈希键对象提供的哈希码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2538092/

相关文章:

java - MultiSet:添加、删除和等于的问题

java - 对变量应用条件 OR

java - 创建唯一对象的列表

java - Spring Security 和 AspectJ Neo4j 存储库 Autowiring

java - 二维阵列战舰放置随机船只

java - 使用键复合键进行高效的 HashMap 检索(由 2 个枚举构建)

java - Firebase:检索实现 Map<K, V> 的类的值

c++ - LeetCode 15:使用 HashMap 的3Sum

java - 根据教师 ID 对学生列表进行排序

java - 在 Java 中将 Collection<SubClass> 传递到期望 Collection<BaseClass> 的函数失败