在 Guava 代码(我认为这是高质量代码的示例之一)中,我发现了以下片段:
// If the cachedHashCode is 0, it will always be recalculated, unfortunately.
private transient int cachedHashCode;
public final int hashCode() {
// Racy single-check.
int code = cachedHashCode;
if (code == 0) {
cachedHashCode = code = element.hashCode();
}
return code;
}
所以“如果cachedHashCode为0,不幸的是它总是会被重新计算”。另一个例子是 JDK String.hashCode
:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
它还会尝试计算一次哈希码,但如果字符串的 hashCode 为 0,则会失败(此类字符串的示例为 "\0"
)。
避免此类重新计算的简单解决方案是添加额外的计算检查:
if (hash == 0) hash++;
虽然在一般情况下它会稍微减慢hashCode
计算速度,但是这个技巧可以避免在一次又一次重复(并且缓慢(例如对于长字符串))计算时避免最坏的情况。
为什么它没有在 guava ImmutableSet
和 JDK String
中使用?
编辑
最近的 Java 7 版本添加了自定义 String.hash32
实现,其中包含对这种特殊情况的处理:
// ensure result is not zero to avoid recalcing
h = (0 != h) ? h : 1;
最佳答案
这样做是为了节省空间。
例如,如果 String
未使用哈希值零来表示(未缓存),则 String
类将需要一个额外的 boolean 标志来表示哈希值未缓存。
因此,权衡是每次重新计算哈希的概率为 40 亿分之一1,而不是为每个字符串对象增加一个单词。
<小时/>1 - 假设从所有可能的 Java 字符串的域中随机选择字符串对象。真实的程序不是这样工作的……但重点是,重新计算哈希码的影响不太可能很大,除非您为此故意设计应用程序。
关于java - 为什么库更喜欢在边缘情况下重新计算不可变对象(immutable对象)的哈希码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20779861/