我有来自 Joshua Bloch 的 effective java 的以下代码(第 9 项,第 3 章,第 49 页)
如果一个类是不可变的,并且计算哈希码的成本是 重要的是,您可以考虑将哈希码缓存在对象中 而不是每次请求时都重新计算。如果你相信 大多数这种类型的对象将被用作哈希键,那么你 应该在创建实例时计算哈希码。 否则,您可能会选择第一次延迟初始化它 hashCode 被调用(第 71 项)。不清楚我们的电话号码 类值得这样处理,但只是为了向您展示它是如何完成的:
// 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
将其分配给结果。关于缓存如何工作的简短解释会很棒。
谢谢
简单。请阅读下面我嵌入的评论...
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;
}
实例变量中的
hashCode
变量,并且未显式初始化,因此 Java 将其初始化为 0
(JLS 第 4.12.5 节)。比较 result == 0
实际上是检查是否已为 result
分配了一个可能非零的哈希码。如果尚未分配,则执行计算,否则仅返回先前计算的哈希码。
如其他答案中已经解释的那样更详细一些,因为许多细节都有特定的含义,尤其是在多线程环境中。
// A field to hold the hash code per instance.
// It's volatile to make it available to all threads. (Otherwise,
// it may happen that the hash code is calculated by one thread, but
// but still not available to other threads, because it remains in
// a cache of a single CPU core.)
private volatile int hashCode;
@Override public int hashCode() {
// Read the field into a local variable in order to not conflict
// with other threads while in this method.
int result = hashCode;
// Check the caches value. 0 means that the hash code has not been
// calculated yet.
if (result == 0) {
// Calculate the hash code, keeping in in a local variable until
// it's finished, to avoid that other threads read an incomplete
// value.
// It's possible that more than one thread calculate the hash code
// simultanoulsy, which could be avoided when it was synchronized.
// In most applications, it performs better when no synchronizing is
// required when accessing an already calculated hash code, which is
// expected to happen much more often compared to the unlikely case
// that two (or more) threads calculate the hash code simulatanoulsy.
// Initialized with 17 also makes sure that the value won't be 0
// after calculation.
result = 17;
result = 31 * result + areaCode;
result = 31 * result + prefix;
result = 31 * result + lineNumber;
// The hash code is written back to the volatile field to make it
// available to future calls.
hashCode = result;
}
// Return the hash code.
return result;
}
最后一些注意事项:
如果您确实希望它正常工作,您可以放置另一个名为 isHashInvalid 的易失性变量布尔值。每个涉及哈希函数中访问的值的设置器都会设置此变量。然后就变成了,(现在不需要测试“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;
}