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

时间:2013-08-27 18:39:14

标签: java caching hashcode effective-java

我有以下来自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将其分配给结果。这个缓存如何工作的简要解释将是伟大的。 感谢

3 个答案:

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