【发布时间】:2010-12-22 14:24:45
【问题描述】:
Eclipse 3.5 有一个非常好的特性来生成Java hashCode() 函数。它会生成例如(稍微缩短:)
class HashTest {
int i;
int j;
public int hashCode() {
final int prime = 31;
int result = prime + i;
result = prime * result + j;
return result;
}
}
(如果类中有更多属性,则为每个附加属性重复result = prime * result + attribute.hashCode();。对于整数,.hashCode() 可以省略。)
这看起来不错,但对于素数的选择 31。它可能取自hashCode implementation of Java String,它是出于性能原因而使用的,在引入硬件乘法器之后早已不复存在。在这里,对于 i 和 j 的小值,您有许多哈希码冲突:例如 (0,0) 和 (-1,31) 具有相同的值。我认为这是一件坏事(TM),因为小值经常出现。对于 String.hashCode,您还会发现许多具有相同哈希码的短字符串,例如“Ca”和“DB”。如果你取一个大素数,如果你选择素数,这个问题就消失了。
所以我的问题是:什么是好的素数?你用什么标准来找到它?
这是一个一般性问题 - 所以我不想给出 i 和 j 的范围。但我想在大多数应用程序中,相对较小的值比较大的值更频繁地出现。 (如果你有很大的值,那么选择素数可能并不重要。)它可能没有太大的区别,但更好的选择是改进这一点的简单而明显的方法 - 那么为什么不这样做呢? Commons lang HashCodeBuilder 也暗示了奇怪的小值。
(澄清:这不是与Why does Java's hashCode() in String use 31 as a multiplier? 的重复,因为我的问题与JDK 31 的历史无关,而是关于会是什么使用相同的基本模板在新代码中获得更好的价值。那里没有一个答案试图回答这个问题。)
【问题讨论】:
-
31 仍然很好,因为它不一定涉及加载常量。在 ARM 处理器上(大约 99.9997% 的手机至少使用一个)
*31可以在一条指令中执行。实际上,任何奇数,无论是否素数都足够好。 -
我在考虑桌面程序,无论您选择 31 还是 1327144003 都无关紧要。奇怪的是,在我的机器上乘以 31 实际上要慢一些 - 可能是优化出了问题。 8-)
-
p = (2^n-1)形式的素数有助于优化编译器通常执行的x * p = (p << n) - p。来自 Joshua Bloch,Effective Java,第 3 章,第 9 项。所以问题 stackoverflow.com/questions/299304/… -
并乘以整数 2^n-1, prime, smallish .. 这给了 31。
-
@MarkRotteveel 请注意,这与 [为什么 Java 的 String 中的 hashCode() 使用 31 作为乘数?][1] 完全不同,因为这不是关于 31 的历史,而是关于什么将是一个更好的选择,而不是使用 31,而不使用额外的库或完全不同的计算哈希的方法。那里的答案都没有解决这个问题。 [1]:stackoverflow.com/questions/299304/…