这里的问题非常简单:有没有办法判断 Java 中的 String
是否被 interned(即是否为字符串池中的字符串)?我的猜测是没有,但我想知道是否有人了解得更好。
你可以通过调用 intern()
方法并检查其是否返回自身来判断一个 String
是否被内部化:
boolean hasBeenInternedBefore = myString.intern() == myString;
这显然有一个缺点,即在之前未对 String
进行内部操作时会进行内部操作。
离题一点,可以使用 Interner
接口 和 Guava 的 Interners
类实现的实现来进行自定义内部操作。
这种方法的优点是,当不再引用 Interner
自身和池时,它们都可以被垃圾回收。
hasBeenInternedBefore = myString == new String(myString).intern()
怎么样? - aioobemyString.intern()
返回的是与 myString
相等的已经 interned 的 String 的引用。它不会改变 myString
中存储的引用!因此结果可能是 false
。 - Andreas DolkhasBeenInternedBefore
为true
。 myString
被内部化并作为自身返回,因此相等性成立。它仅测试contentsHaventBeenInternedBeforeAsAnotherStringObject
。 - Dan Getz有一种方法可以检查特定的String
对象是否已经被内部化,但如果这些内容尚未被内部化,则会将其插入到字符串池中。创建一个具有相同内容的新String
对象,将其内部化,并将其与原始对象进行比较:
new String(s).intern() == s
new String(s) != s
。考虑每种可能的情况:
s
在字符串池中。 new String(s)
与s
具有相同的内容,因此对其调用intern()
将返回s
。表达式的结果为true
。s
不在字符串池中,但另一个相等的String
对象在其中——我们称之为s2
。 intern()
将返回s2
,因此表达式的结果为false
。s
不在字符串池中,也没有任何等于它的String
。在这种情况下,new String(s)
将被存储到字符串池中,这会修改字符串池。由于这不是与s
相同的String
对象,表达式的结果为false
。s
是否在字符串池中。以下测试演示了这一点:public static void main(String[] args) {
String interned = new String(new char[] { 'i', 'n', 't' }).intern();
String notInterned = new String(new char[] { 'n', 'o', 't' });
System.out.println("Case 1: " + wasInterned(interned));
System.out.println("Case 2: " + wasInterned(new String(interned)));
System.out.println("Case 3: " + wasInterned(notInterned));
}
public static boolean wasInterned(String s) {
return new String(s).intern() == s;
}
Case 1: true
Case 2: false
Case 3: false
intern
来确保或不要依赖于内部化。如果你需要用于测试或实验的内部化或非内部化字符串,可以按照以下方法创建它们:s = someArbitraryString.intern();
非内部化:
s = new String(someArbitraryString);
我们无法查看内部字符串存储库,也无法获取所有已存储的字符串集合。
测试一个给定的字符串是否已经被存储会产生一个棘手的问题(顺便说一下,这是测量中常见的问题):测试本身可能会影响内部字符串存储库;)
为了测试一个存储库是否包含一个给定的字符串,我们需要将该字符串与存储库中的所有字符串进行比较(最坏情况),有风险的是,JVM在我们开始比较之前会对该引用字符串进行存储,这将返回一个“true”,尽管在测试之前该字符串并未被存储;)
但除此之外,我没有看到知道虚拟机是否已经存储了一个字符串的任何实际用途。存储是足够便宜的,如果需要,就存储它。 (如果有实际用途,String类将提供本地测试方法)