Joshua Bloch在有效的java中建议如何在Java中使用缓存哈希码?

我有以下来自Joshua Bloch的有效java代码(第9章,第3章,第49页)

如果类是不可变的并且计算哈希代码的成本很高,您可以考虑在对象中缓存哈希代码,而不是在每次请求时重新计算它。 如果您认为此类型的大多数对象将用作哈希键,则应在创建实例时计算哈希码。 否则,您可能会在第一次调用hashCode时选择懒惰地初始化它(Item 71)。 目前尚不清楚我们的PhoneNumber课程是否值得这样做,只是为了向您展示它是如何完成的:

// 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是否已经分配了大概非零哈希码。 如果尚未分配,则执行计算,否则它只返回先前计算的哈希码。

如果你真的希望这个能正常工作,你可以使用另一个名为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; }