8

我有以下来自 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将其分配给结果。关于这种缓存如何工作的简要说明会很棒。谢谢

4

3 回答 3

12

简单的。阅读下面我嵌入的评论...

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;
   }
于 2013-08-27T18:51:24.043 回答
2

hashCode实例变量中的变量,并且没有显式初始化,因此Java 将其初始化为0(JLS Section 4.12.5)。比较result == 0实际上是检查是否result已经分配了一个可能非零的哈希码。如果尚未分配,则执行计算,否则仅返回先前计算的哈希码。

于 2013-08-27T18:41:55.240 回答
-1

如果您真的希望它正常工作,您可以放置​​另一个名为 isHashInvalid 的 volatile 变量 boolean。每个涉及在哈希函数中访问的值的设置器都会设置此变量。然后它变成了,(现在不需要测试'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;
}
于 2013-09-29T23:57:37.120 回答