我有以下来自Joshua Bloch的有效java代码(第9章,第3章,第49页)
如果一个类是不可变的并且计算哈希码的成本是 重要的是,您可以考虑在对象中缓存哈希码 而不是每次请求时重新计算它。如果你相信 大多数此类对象将用作哈希键,然后你 应该在创建实例时计算哈希码。 否则,您可能会选择第一次懒洋洋地初始化它 调用hashCode(Item 71)。目前尚不清楚我们的PhoneNumber class值得这种处理,但只是为了告诉你它是如何完成的:
// Lazily initialized, cached hashCode
private volatile int hashCode; // (See Item 71)
@Override public int hashCode() {
int result = hashCode;
if (result == 0) {
result = 17;
result = 31 * result + areaCode;
result = 31 * result + prefix;
result = 31 * result + lineNumber;
hashCode = result;
}
return result;
}
我的问题是如何缓存(记住hashCode)在这里工作。第一次调用hashCode()
方法时,没有hashCode
将其分配给结果。这个缓存如何工作的简要解释将是伟大的。
感谢
答案 0 :(得分:10)
简单。阅读下面的嵌入式评论......
private volatile int hashCode;
//You keep a member field on the class, which represents the cached hashCode value
@Override public int hashCode() {
int result = hashCode;
//if result == 0, the hashCode has not been computed yet, so compute it
if (result == 0) {
result = 17;
result = 31 * result + areaCode;
result = 31 * result + prefix;
result = 31 * result + lineNumber;
//remember the value you computed in the hashCode member field
hashCode = result;
}
// when you return result, you've either just come from the body of the above
// if statement, in which case you JUST calculated the value -- or -- you've
// skipped the if statement in which case you've calculated it in a prior
// invocation of hashCode, and you're returning the cached value.
return result;
}
答案 1 :(得分:2)
实例变量中的hashCode
变量,并未明确初始化so Java intializes it to 0
(JLS Section 4.12.5)。比较result == 0
实际上是检查是否已为result
分配了可能的非零哈希码。如果尚未分配,则执行计算,否则只返回先前计算的哈希码。
答案 2 :(得分:0)
如果你真的希望这个能够正常工作,那么你需要另外一个名为isHashInvalid的volatile变量boolean。涉及在哈希函数中访问的值的每个setter都将设置此变量。然后变成,(现在不需要测试'0'):
private volatile int isHashInvalid=TRUE;
private volatile int hashCode; //Automatically zero but it doesn't matter
//You keep a member field on the class, which represents the cached hashCode value
@Override public int hashCode() {
int result = hashCode;
if (isHashInvalid) {
result = 17;
result = 31 * result + areaCode;
result = 31 * result + prefix;
result = 31 * result + lineNumber;
//remember the value you computed in the hashCode member field
hashCode = result;
isHashInvalid=FALSE;
}
// when you return result, you've either just come from the body of the above
// if statement, in which case you JUST calculated the value -- or -- you've
// skipped the if statement in which case you've calculated it in a prior
// invocation of hashCode, and you're returning the cached value.
return result;
}