我观察到了
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);
}
}
从你的帖子和评论中我可以看出
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
。