HashMap#hash(int)方法的说明

有人可以向我解释静态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); } 

一个例子可以让它更容易消化。

澄清我知道运算符,真值表和按位运算。 我真的无法真正解码实现或评论。 甚至背后的推理。

>>>是逻辑右移(无符号扩展)( JLS 15.19移位运算符 ), ^是按位异或 – ( JLS 15.22.1整数位运算符 )。

至于为什么要这样做,文档提供了一个提示: 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是素数)。

我不知道所有转变是如何起作用的,但动机在评论中列出:

实现HashMap的方式依赖于hashCode函数的充分实现。 特别是,散列值的低位应该均匀分布。 如果在较低位上有许多冲突,则HashMap将无法正常运行。

因为hashCode的实现超出了HashMap的控制范围(每个对象都可以实现它们自己的),所以它们提供了一个额外的哈希函数,它将对象的hashCode稍微移动一点,以确保较低的位更随机地分布。 同样,我不知道它是如何工作的(或者它有多有效),但我认为它至少取决于更高的位被均等地分配(它似乎将较高位网格化为较低位)。

所以这样做是为了在存在实现不良的hashCode方法的情况下尽量减少冲突(从而提高性能)。