根据这个博客条目,HashMap在它已经检索到的hashcode上重新调用它自己的hashCode()
实现(称为hash()
)。
如果key不为空,那么它将在key对象上调用hashfunction,参见上面方法中的第4行,即key. hashcode(),因此在key. hashcode()返回hashValue之后,第4行看起来像
int hash = hash(hashValue)
,现在,它将返回的hashValue应用到自己的哈希函数中。
我们可能想知道为什么要使用hash(hashvalue)再次计算哈希值。答案是,它可以防止劣质散列函数
HashMap 可以准确地重新分配哈希码吗?HashMap可以存储对象,但是它不能访问为hashCode分配对象的逻辑。例如,hash()
不可能集成以下hashCode()
实现背后的逻辑:
public class Employee {
protected long employeeId;
protected String firstName;
protected String lastName;
public int hashCode(){
return (int) employeeId;
}
}
hash()
从实际的哈希码中派生出"改进的"哈希码,因此相等的输入总是相等的输出(来自jdk1.8.0_51):
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
至于为什么哈希代码需要改进,请阅读该方法的javadoc:
计算key.hashCode()并将哈希的高位扩展到低位。因为表使用了2的幂掩码,所以在当前掩码之上只变化几比特的哈希集总是会发生冲突。(其中一个已知的例子是在小表格中保存连续整数的Float键集。)因此,我们应用一个变换,将高比特的影响向下扩散。比特传播的速度、效用和质量之间存在权衡。因为许多常见的哈希集已经合理分布(因此不会从扩展中受益),并且因为我们使用树来处理箱中的大型碰撞集,所以我们只是以最便宜的方式对一些移位的位进行异或,以减少系统损失,同时考虑到由于表边界而在索引计算中永远不会使用的最高位的影响。