为啥 Java 使用 (hash & 0x7FFFFFFF) % tab.length 来决定键的索引?

Posted

技术标签:

【中文标题】为啥 Java 使用 (hash & 0x7FFFFFFF) % tab.length 来决定键的索引?【英文标题】:Why does Java use (hash & 0x7FFFFFFF) % tab.length to decide the index of a key?为什么 Java 使用 (hash & 0x7FFFFFFF) % tab.length 来决定键的索引? 【发布时间】:2012-03-11 22:43:45 【问题描述】:

从下面的链接中,我知道 Java 使用 (hash & 0x7FFFFFFF) % tab.length 来决定将 key, value 放入数组的哪个槽。

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/Hashtable.java#Hashtable.put%28java.lang.Object%2Cjava.lang.Object%29

我的问题是为什么 Java 首先做 hash & 0x7FFFFFFF?有什么特别的目的吗?

【问题讨论】:

我认为这是为了确保键始终是正整数,这样您就不会尝试使用负数组索引。 假设它不会。例如,-12345678 % 37 是多少? 是 ConcurrentHashMap 的 "hash & (tab.length - 1)" 和 hashtable 的 "(hash & 0x7FFFFFFF) % tab.length" 一样吗? 【参考方案1】:

因为-1 % 10 == -1 您肯定不希望将其索引到数组中。强制符号位为 0 可以避免这个问题。

【讨论】:

是的,该死的,我忘记了可能的负哈希码。谢谢 @plzdontkillme 因为 math.abs 并不总是返回正数。此外,取决于编译器和架构,它的效率会更高或更高。 谢谢 Voo,非常感谢 @plzdontkillme 详细说明,Math.abs(-2^-31) 是 -2^-31,因为没有 2^31。【参考方案2】:

请注意,Hashtable 或多或少已经过时,已被HashMap 取代。这个使用hash & (table.length-1)来达到同样的目的。

它之前也做了一些位移,可以看到here。这是为了应对 hashCode() 方法的错误实现,该方法返回的数字多样性较低。

【讨论】:

【参考方案3】:

因为:

0x7FFFFFFF 是 0111 1111 1111 1111 1111 1111 1111 1111 :除符号位外全为 1。

(hash & 0x7FFFFFFF) 将产生一个正整数。

(hash & 0x7FFFFFFF) % tab.length 将在制表符长度范围内。

【讨论】:

+1 很好的解释。然而我有一个疑问。 ConcurrentHashMap 的“hash & (tab.length - 1)”是否与 hashtable 的“(hash & 0x7FFFFFFF) % tab.length”相同? 很好的解释! 完美的解释。我什至不需要这个代码,但对于类似的我正在看的东西是有意义的。谢谢!

以上是关于为啥 Java 使用 (hash & 0x7FFFFFFF) % tab.length 来决定键的索引?的主要内容,如果未能解决你的问题,请参考以下文章

神奇的魔法数字0x61c88647

Hash

为啥HashMap的运算复杂度没有考虑hash函数的复杂度? [复制]

哈希表哈希避免负哈希码

为啥使用十六进制?

[白话解析] 深入浅出一致性Hash原理