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 序列)。由于这种 AND 运算,仅使用 h 的低位。 h 的其余部分将被忽略。想象一下,无论出于何种原因,原始哈希仅返回能被2整除的数字。如果直接使用它,则 HashMap 的奇数位置将永远不会被使用,导致冲突次数增加2倍。在真正病态的情况下,错误的哈希函数可能会使 HashMap 的行为更像是列表,而不是 O(1) 容器。

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

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

相关文章:

java - 从 HashMap 获取第一个和最后一个元素

java - Android 的属性文件

java - 一起使用 EJB3 和 Struts 2

url - 为 Backbone Collection 使用动态 url

java - 我可以结合按功能分组对流进行分区吗?

java - 具有多个值键的 HashMap

Java 8 forEach 带索引

java - 如何使用 iBatis 将数组写入 Oracle 10g XE 数据库?

Java 8 : Stream a list and map to another list based on different filters

java - 在Java中反向HashMap键和值