更改为Java8中的HashMap哈希函数

更改为Java8中的HashMap哈希函数,java,performance,hash,hashmap,bit-manipulation,Java,Performance,Hash,Hashmap,Bit Manipulation,在java 8 java.util.Hashmap中,我注意到一个变化: : 从代码中可以看出,新函数是较简单的低16位异或,高16位保持高16位不变,而不是先前实现中的几个不同移位,从评论中可以看出,这在将低比特中具有大量冲突的哈希函数的结果分配给不同的存储桶时效率较低,但由于需要执行较少的操作,因此节省了CPU周期 我在发行说明中看到的唯一一件事是从链表到平衡树存储冲突键(我认为这可能改变了计算一个好的散列所花费的时间),我特别感兴趣的是看看这种变化是否会对大型散列映射产生任何预期的性能影响

在java 8 java.util.Hashmap中,我注意到一个变化:

:

从代码中可以看出,新函数是较简单的低16位异或,高16位保持高16位不变,而不是先前实现中的几个不同移位,从评论中可以看出,这在将低比特中具有大量冲突的哈希函数的结果分配给不同的存储桶时效率较低,但由于需要执行较少的操作,因此节省了CPU周期


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

如您所述:Java 8中的
哈希映射
有显著的性能改进,如中所述。基本上,如果散列链超过某个大小,则
HashMap
将(在可能的情况下)用一个平衡的二叉树替换它。这使得各种操作的“最坏情况”行为
O(logn)
而不是
O(N)

这并不能直接解释对
散列的更改。然而,我假设JEP-180中的优化意味着由于散列函数分布不均匀而导致的性能损失不那么重要,并且
散列方法的成本效益分析发生了变化;i、 e.一般来说,更复杂的版本不太有利。(请注意,当密钥类型的
hashcode
方法生成高质量代码时,那么在复杂版本的
hash
方法中进行体操是浪费时间的。)


但这只是一个理论。
散列
更改的真正原因很可能是Oracle机密。

当我运行散列实现差异时,我看到以下以纳秒为单位的时间差异(不太大,但在大小巨大~100万+)时可能会产生一些影响-

7473 ns–java 7

3981 ns–java 8


如果我们讨论的是格式良好的键和大尺寸(~百万)的hashmap,这可能会产生一些影响,这是因为简化了逻辑

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

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

当“哈希链”的大小超过限制时,它将从特定的链接列表转移到“平衡树”。因此,最坏情况下的操作需要O(n)时间,而不是O(n)。@darkdefender27-你的评论没有意义。1) O(n)比O(n)好多少?2) 它实际上去了O(logn)!3) 这就是我在回答中所说的……哦,对不起,我的意思是O(logn)。你的回答完全有道理。我只是想声明它会切换到“平衡”二叉搜索树。我已经添加了这个。谢谢。你从哪里得到1000+的数字?无论如何,一个现实世界的用例是,哈希表密钥由客户端代码生成,而有人(黑客)故意用相同的哈希代码制造密钥,以填充一个存储桶,从而实施一种形式的拒绝服务攻击!
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);