为什么 String.intern() 似乎对相同的字符串返回不同的实例?

问题描述 投票:0回答:1

我观察到了

String.intern()
的行为,我正在尝试理解它。这似乎与该方法的文档相矛盾。

private static String buildSampleString() {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < 10; i++) {
        builder.append((char)(i + 'a'));
    }
    return builder.toString();
}

private static void performTest(String a) {
    String b = buildSampleString().intern();
    System.out.println("a vs. b: " + (a == b) + ", " + a.equals(b));
    System.out.println(b + ": " + System.identityHashCode(b));
}

public static void main(String[] args) {
    String a = buildSampleString();
    performTest(a);
    performComputation(); // see below for details
    performTest(a);
}
每次调用时,

buildSampleString()
都会生成新的、相等的字符串。其中一个实例
a
在程序的整个生命周期中都会保留。
performTest(a)
构建一个新的
b
,对其进行实习,然后将其与
a
进行比较(无论是同一性还是平等性),正如预期的那样,它们相等但不相同。后者是因为
a
没有被实习过。

String.intern()
的文档说:

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

根据

equals()
的传递性,两个
b
字符串是相等的,因此根据文档,它们是相同的。因此,调用
System.identityHashCode(b)
应返回相同的值。有时确实如此,但前提是
performComputation()
在中间不做太多工作。如果它确实工作得太辛苦——我怀疑它与堆的颠簸有关——那么
System.identityHashCode(b)
第二次返回不同的值......如果文档正确的话,这应该是不可能的。

这是

performComputation
的代码:

private static final Random random = new Random();

private static String randomString() {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < 10000000; i++) {
        builder.append((char)(random.nextInt(127 - 32) + 32));
    }
    return builder.toString();
}

private static void performComputation() {
    for (int i = 0; i < 10; i++) {
        String s = randomString();
        System.out.println(s.substring(0, 3) + "..." + s.substring(s.length() - 3));
    }
}

如果我将循环从 10000000 次迭代更改为 10 次,那么我会得到相同的身份哈希。

这里到底发生了什么?

编辑:重现行为的完整代码:

import java.util.Random;

public class Main {

    private static final Random random = new Random();

    private static String randomString() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 10000000; i++) {
            builder.append((char)(random.nextInt(127 - 32) + 32));
        }
        return builder.toString();
    }

    private static void performComputation() {
        for (int i = 0; i < 10; i++) {
            String s = randomString();
            System.out.println(s.substring(0, 3) + "..." + s.substring(s.length() - 3));
        }
    }

    // ----------------------------------------------------------------------------------------------------------------

    private static String buildSampleString() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            builder.append((char)(i + 'a'));
        }
        return builder.toString();
    }

    private static void performTest(String a) {
        String b = buildSampleString().intern();
        System.out.println("a vs. b: " + (a == b) + ", " + a.equals(b));
        System.out.println(b + ": " + System.identityHashCode(b));
    }

    public static void main(String[] args) {
        String a = buildSampleString();
        performTest(a);
        performComputation();
        performTest(a);
    }
}
java string equals identity
1个回答
0
投票

从你的帖子和评论中我可以看出

intern() 的文档声称对于相等的字符串返回相同的对象

你的误解始于这样的事实

String a = buildSampleString();
String b = buildSampleString().intern();
System.out.println("a vs. b: " + (a == b) + ", " + a.equals(b));

退货

a vs. b: false, true

换句话说,为什么

a
b
引用不同的对象?

每次调用

buildSampleString
都会返回一个新对象。第一次调用被分配给
a
。你的程序中的任何内容都不会改变(重新分配)
a
。第二次调用也返回一个新对象,然后
intern
将其添加到池中并返回对其的引用,该引用存储在
b
中。

a
b
引用不同的对象。因此
==
将返回
false

© www.soinside.com 2019 - 2024. All rights reserved.