【问题标题】:Is the hashcode of a Java String created when the String is created, or only at the first call for hashcode?Java 字符串的哈希码是在创建字符串时创建的,还是仅在第一次调用哈希码时创建?
【发布时间】:2020-08-03 20:00:33
【问题描述】:

我的问题很简单。

什么时候计算字符串的哈希码?

  1. 创建字符串时,还会计算哈希码,并且在构造完成后总是在 O(1) 内准备就绪
  2. 仅在第一次调用 hashCode 方法时计算哈希码,并在 O(1) 时间内为所有后续调用做好准备
  3. 每次调用hashCode 方法时都会计算哈希码

选项 1 似乎是合理的,因为字符串是不可变的。给定字符串的哈希码永远不会改变。但是,这会减慢字符串的创建速度,因此使用选项 2 似乎也是合理的。选项 3 看起来很傻而且浪费时间,但是因为没有存储哈希码,所以可以节省空间。选项 3 是我没有考虑过的最佳方法可能还有一些合乎逻辑的原因。

提前非常感谢

【问题讨论】:

  • @khelwood 真的吗?调用 hashCode 后,String 对象的哈希码以某种方式被缓存?缓存在哪里?在对象?我认为是选项 3
  • 您可以查看String 的源代码,看看它是在第一次调用时计算和缓存的。 *如何*这与不变性一起工作是另一回事,最好留给专家。简短的回答是它有点不像。如果两个线程及时调用hashCode(),则很有可能两者最终都会分别计算哈希码的值。这是可行的,因为字符串字符永远不会改变,因此将始终计算相同的哈希码。
  • 请注意,Java Strings 的哈希码不会在任何这些点计算;他们只是发生。 OpenJDK 使用 (2) 的事实是一个实现细节。
  • @chrylis-cautiouslyoptimistic- 我不确定你的意思。如果你的意思是“hashCode 的缓存行为不是外部指定的”,是的,这是公平的。如果需要,它当前的实现可能会改变。但是,在 Java 25 年多的历史中,hashCode 一直是这样计算的。

标签: java string


【解决方案1】:

选项 2。在第一次调用 hashCode 时计算并存储在私有字段中。

在 OpenJDK 8 中它看起来像这样:

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

http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/String.java#l1452

事实上,如果你看一下String有哪些字段,你可以看到它有一个名为hash的私有int。

System.out.println(String.class.getDeclaredFields());

输出包括

{ ... private int java.lang.String.hash ... }

【讨论】:

    【解决方案2】:

    来自JDK 14的源代码

       /** Cache the hash code for the string */
        private int hash; // Default to 0
    
    
        @HotSpotIntrinsicCandidate
        public String(String original) {
            this.value = original.value;
            this.coder = original.coder;
            this.hash = original.hash;
        }
    

    然后

        public int hashCode() {
            // The hash or hashIsZero fields are subject to a benign data race,
            // making it crucial to ensure that any observable result of the
            // calculation in this method stays correct under any possible read of
            // these fields. Necessary restrictions to allow this to be correct
            // without explicit memory fences or similar concurrency primitives is
            // that we can ever only write to one of these two fields for a given
            // String instance, and that the computation is idempotent and derived
            // from immutable state
            int h = hash;
            if (h == 0 && !hashIsZero) {
                h = isLatin1() ? StringLatin1.hashCode(value)
                               : StringUTF16.hashCode(value);
                if (h == 0) {
                    hashIsZero = true;
                } else {
                    hash = h;
                }
            }
            return h;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-22
      • 1970-01-01
      • 1970-01-01
      • 2015-10-11
      • 2011-01-01
      • 2020-06-05
      • 2010-11-15
      相关资源
      最近更新 更多