正如Joshua Bloch在《高效Java》中所建议的那样,缓存哈希代码在Java中是如何工作的?

正如Joshua Bloch在《高效Java》中所建议的那样,缓存哈希代码在Java中是如何工作的?,java,caching,hashcode,effective-java,Java,Caching,Hashcode,Effective Java,我有约书亚·布洛赫(Joshua Bloch)的有效java中的以下代码(第3章第9项,第49页) 如果一个类是不可变的,并且计算哈希代码的成本是 重要的是,您可以考虑缓存对象中的哈希代码。 而不是每次请求时都重新计算它。如果你相信 这种类型的大多数对象都将用作哈希键,然后 应在创建实例时计算哈希代码。 否则,您可能会选择第一次惰性地初始化它 调用hashCode(项目71)。我们的电话号码不清楚 这门课值得这样对待,但只是为了向你们展示它是如何做到的: 我的问题是缓存(记住hashCode)在

我有约书亚·布洛赫(Joshua Bloch)的有效java中的以下代码(第3章第9项,第49页)

如果一个类是不可变的,并且计算哈希代码的成本是 重要的是,您可以考虑缓存对象中的哈希代码。 而不是每次请求时都重新计算它。如果你相信 这种类型的大多数对象都将用作哈希键,然后 应在创建实例时计算哈希代码。 否则,您可能会选择第一次惰性地初始化它 调用hashCode(项目71)。我们的电话号码不清楚 这门课值得这样对待,但只是为了向你们展示它是如何做到的:

我的问题是缓存(记住hashCode)在这里是如何工作的。第一次调用
hashCode()
方法时,没有
hashCode
将其分配给结果。简单解释一下这种缓存是如何工作的就好了。
感谢实例变量中的
hashCode
变量,它没有显式初始化。比较
result==0
实际上是检查
result
是否分配了一个假定为非零的散列码。如果尚未分配,则执行计算,否则只返回先前计算的哈希代码。

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

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

如果您真的想让它正常工作,您应该放置另一个名为isHashInvalid的可变布尔变量。每个涉及哈希函数中访问的值的setter都会设置此变量。然后它变为,(现在不需要测试“0”):


缓存意味着保存已计算的值,以便无需重新计算即可重复使用。就这么多,嗯?缓存是“私有volatile int hashCode”。计算哈希时,它会保存到缓存中。最初的值是0,因为所有的非局部数值变量都是0。我不清楚增加一个额外的整数是否值得。使用零的唯一问题是,如果
hashCode
可以返回零,那么哨兵就可以解决这个问题,只需在计算
结果后说
如果(result==0)result=8675309+areacode。它要么是64位数字,要么是32位数字。它实际变为零的次数很低。因此,对于数量无限小的项目,每次都会计算哈希代码。没什么大不了的。除非我误解了,否则您使用的是
isHashInvalid
,目的是允许零成为有效的散列值,而无需对其进行重新哈希。我的观点是,如果一个人担心每次都必须重新刷新某些对象的风险(对于任意大小的对象,应该是,尽管可能不是本例中固定大小的对象),那么他不需要使用额外的标志来防止这种情况。即使他想允许增量哈希(例如,将数据添加到已经计算了散列的列表中,只需计算新项目的散列)使用31位散列似乎比使用额外的标志要好(如果散列是好的,即使没有第32位,错误命中也应该很少,如果不是好的,第32位也不太可能有帮助)为什么要使用修饰符
volatile
?与缓存相关的内容?如果是,请给出简要说明。谢谢。@Charan完全没有必要(如果您查看
java.lang.String
源代码,
hash
字段不是
volatile
不易失性的唯一缺点是,在不同CPU上运行的线程可能会多次重新计算hashcode。但是,由于字符串在java中是不可变的,因此不会导致任何不一致性,只是可能的执行ance惩罚,我想这是可以的,因为散列的读取频率比计算的要高(与正常读取相比,易失性读取可能有很大的开销)。
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;
   }
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;
}