java - 为什么库更喜欢在边缘情况下重新计算不可变对象(immutable对象)的哈希码?

标签 java caching guava immutability hashcode

在 Guava 代码(我认为这是高质量代码的示例之一)中,我发现了以下片段:

// If the cachedHashCode is 0, it will always be recalculated, unfortunately.
private transient int cachedHashCode;

public final int hashCode() {
   // Racy single-check.
  int code = cachedHashCode;
  if (code == 0) {
    cachedHashCode = code = element.hashCode();
  }
  return code;
}

所以“如果cachedHashCode为0,不幸的是它总是会被重新计算”。另一个例子是 JDK String.hashCode:

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

它还会尝试计算一次哈希码,但如果字符串的 hashCode 为 0,则会失败(此类字符串的示例为 "\0")。

避免此类重新计算的简单解决方案是添加额外的计算检查:

if (hash == 0) hash++;

虽然在一般情况下它会稍微减慢hashCode计算速度,但是这个技巧可以避免在一次又一次重复(并且缓慢(例如对于长字符串))计算时避免最坏的情况。 为什么它没有在 guava ImmutableSet 和 JDK String 中使用?

编辑

最近的 Java 7 版本添加了自定义 String.hash32 实现,其中包含对这种特殊情况的处理:

       // ensure result is not zero to avoid recalcing
       h = (0 != h) ? h : 1;

最佳答案

这样做是为了节省空间。

例如,如果 String 未使用哈希值零来表示(未缓存),则 String 类将需要一个额外的 boolean 标志来表示哈希值未缓存。

因此,权衡是每次重新计算哈希的概率为 40 亿分之一1,而不是为每个字符串对象增加一个单词。

<小时/>

1 - 假设从所有可能的 Java 字符串的域中随机选择字符串对象。真实的程序不是这样工作的……但重点是,重新计算哈希码的影响不太可能很大,除非您为此故意设计应用程序。

关于java - 为什么库更喜欢在边缘情况下重新计算不可变对象(immutable对象)的哈希码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20779861/

相关文章:

ruby-on-rails - 在不重新查询数据库的情况下重新排序 Rails 中的事件记录?

ios - iOS 会自动清理缓存目录吗?

java - 如何同时共享 Java 和 GWT 项目的 EventBus?

java - 数字/货币格式

java - 使用 Java 接收的消息更新 JSP(编写消息客户端)

java - 使用 Selenium Grid 时的 ChromeDriver 日志记录

java - Guava ListMultimap中put()和get()操作的时间复杂度是多少?

java - 可以阻止 JIT 优化方法吗?

asp.net-mvc - ASP.NET MVC : Clear an action's cache from another action

java - Guava 表并发修改异常