在Java 8中更改为HashMap哈希函数

在java 8 java.util.Hashmap中,我发现了一个变化:

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

到 :

 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 

从代码中可以看出,新函数是低16位的简单XOR ,高16位保持高16位不变,与之前实现中的几个不同的位移相反,并且从评论中看,这不太有效。将较低位的大量冲突的散列函数的结果分配给不同的桶,但通过减少操作来节省CPU周期。

我在发行说明中看到的唯一一件事就是从链接列表到平衡树的变化以存储碰撞键(我认为这可能会改变计算好哈希的时间量),我特别感兴趣的是看到如果此更改对大型哈希映射有任何预期的性能影响。 是否有关于此更改的任何信息,或者是否有更好的哈希函数知识的人知道此更改的含义可能是什么(如果有的话,我可能只是误解了代码)以及是否需要生成哈希迁移到Java 8时以不同的方式编码以保持性能?

如您所述:如JEP-180中所述,Java 8中的HashMap有显着的性能提升。 基本上,如果哈希链超过一定大小, HashMap将(在可能的情况下)用平衡二叉树替换它。 这使得各种操作O(log N)的“最坏情况”行为代替O(N)

这并不能直接解释hash的变化。 但是,我假设 JEP-180中的优化意味着由于分布不良的哈希函数导致的性能损失不那么重要,并且hash方法的成本效益分析会发生变化; 即更复杂的版本平均不太有利。 (在绑定中,当密钥类型的hashcode码方法生成高质量代码时, hash方法的复杂版本中的体操是浪费时间。)

但这只是一种理论。 hash更改的真正原理很可能是Oracle机密。

当我运行哈希实现diffences时,我看到纳秒时间差异如下(不是很好,但是当大小很大〜1百万+时会有一些效果) –

7473 ns – java 7

3981 ns- java 8

如果我们谈论格式正确的密钥和大尺寸的哈希图(〜百万),这可能会产生一些影响,这是因为简化了逻辑。

正如Java文档所说,这个想法是为了处理链接列表的旧实现执行O(n)而不是O(1)的情况。 当为插入HashMap的大量数据生成相同的哈希码时,会发生这种情况。

但这不是正常的情况。 为了处理一旦哈希桶中的项目数量增长超过某个阈值,该桶将从使用链接的条目列表切换到二叉树的情况。 在高哈希冲突的情况下,这将提高从O(n)到O(log n)的搜索性能,这更好并且解决了性能问题。