有人可以向我解释静态HashMap #hash(int)方法吗?
生成均匀分布的哈希值背后的理由是什么?
/**
* Applies a supplemental hash function to a given hashCode, which
* defends against poor quality hash functions. 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. Note: Null keys always map to hash 0, thus index 0.
*/
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
一个例子可以让它更容易消化。
澄清 我知道运算符,真值表和按位运算。我真的无法真正解码实现或评论。甚至背后的推理。
答案 0 :(得分:16)
>>>
是逻辑右移(无符号扩展)(JLS 15.19 Shift Operators),^
是按位异或 - (JLS 15.22.1 Integer Bitwise Operators)。
至于为什么要这样做,文档提供了一个提示:HashMap
使用两个幂的长度表,并通过屏蔽高位并仅取其哈希码的低位来散列键。
// HashMap.java -- edited for conciseness
static int indexFor(int h, int length) {
return h & (length-1);
}
public V put(K key, V value) {
int hash = hash(key.hashCode());
int index = indexFor(hash, table.length);
// ...
}
所以hash()
尝试将相关性带到更高的位,否则会被屏蔽掉(indexFor
基本上丢弃h
的高位并仅采用较低的k
}位length == (1 << k)
)。
将此与Hashtable
(应该不具有两个幂的长度表)的方式对比使用密钥的哈希码。
// Hashtable.java -- edited for conciseness
public synchronized V get(Object key) {
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % table.length;
// ...
}
通过执行更昂贵的%
操作(而不是简单的位掩码),Hashtable
的性能对低位分布较差的哈希码不太敏感(特别是table.length
是一个素数)。
答案 1 :(得分:2)
我不知道所有转变是如何起作用的,但动机在评论中列出:
实现HashMap的方式依赖于hashCode函数的充分实现。特别是,散列值的低位应该均匀分布。如果低位上有很多碰撞,HashMap将无法正常运行。
因为hashCode的实现超出了HashMap的控制范围(每个对象都可以实现它们自己的),所以它们提供了一个额外的哈希函数,它将对象的hashCode稍微移动一点,以确保较低的位更随机地分布。同样,我不知道它是如何工作的(或它有多有效),但我认为它至少取决于更高的位被均匀分布(它似乎将较高位网格化为较低位)。
所以这样做是为了在存在实现不当的hashCode方法的情况下尽量减少冲突(从而提高性能)。