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; }