Java - 如何判断一个字符串是否被interned?

16

这里的问题非常简单:有没有办法判断 Java 中的 String 是否被 interned(即是否为字符串池中的字符串)?我的猜测是没有,但我想知道是否有人了解得更好。


3
我想知道为什么你需要这些信息?如果是为了优化而避免不必要的intern调用,我建议你不要这么做,直接进行intern处理即可。 - Mikko Wilkman
4个回答

16

6
至少你会知道这只猫是不是活着的。 - user166390
1
hasBeenInternedBefore = myString == new String(myString).intern()怎么样? - aioobe
1
@lechlukasz - 不完全是这样,myString.intern() 返回的是与 myString 相等的已经 interned 的 String 的引用。它不会改变 myString 中存储的引用!因此结果可能是 false - Andreas Dolk
1
@dma_k:是的,我甚至在第三段中提到了这个事实。 - Joachim Sauer
1
至少在Java 7上,这会返回错误的结果。如果字符串的内容以前从未被内部化过,则hasBeenInternedBeforetruemyString被内部化并作为自身返回,因此相等性成立。它仅测试contentsHaventBeenInternedBeforeAsAnotherStringObject - Dan Getz
显示剩余13条评论

3

有一种方法可以检查特定的String对象是否已经被内部化,但如果这些内容尚未被内部化,则会将其插入到字符串池中。创建一个具有相同内容的新String对象,将其内部化,并将其与原始对象进行比较:

new String(s).intern() == s

这是因为new String(s) != s。考虑每种可能的情况:
  1. s在字符串池中。 new String(s)s具有相同的内容,因此对其调用intern()将返回s。表达式的结果为true
  2. s不在字符串池中,但另一个相等的String对象在其中——我们称之为s2intern()将返回s2,因此表达式的结果为false
  3. 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

0
一般来说,你不需要进行检查 - 只需将它们 intern 来确保或不要依赖于内部化。如果你需要用于测试或实验的内部化或非内部化字符串,可以按照以下方法创建它们:
内部化:
s = someArbitraryString.intern();

非内部化:

s = new String(someArbitraryString);

0

我们无法查看内部字符串存储库,也无法获取所有已存储的字符串集合。

测试一个给定的字符串是否已经被存储会产生一个棘手的问题(顺便说一下,这是测量中常见的问题):测试本身可能会影响内部字符串存储库;)

为了测试一个存储库是否包含一个给定的字符串,我们需要将该字符串与存储库中的所有字符串进行比较(最坏情况),有风险的是,JVM在我们开始比较之前会对该引用字符串进行存储,这将返回一个“true”,尽管在测试之前该字符串并未被存储;)

但除此之外,我没有看到知道虚拟机是否已经存储了一个字符串的任何实际用途。存储是足够便宜的,如果需要,就存储它。 (如果有实际用途,String类将提供本地测试方法)


实习生的成本较低,但如果一个应用程序处理大量字符串,内存消耗可能会成为一个问题。 - Adriaan Koster
据我所知,当太多字符串被内部化时可能会出现问题,可能是因为它们落在PermSpace中。在这种情况下,考虑使用Guava的WeakInterner。 - maaartinus
1
@maaartinus即使在今天,在HotSpot JVM中,没有PermGen空间,字符串池的表仍然具有固定大小,在旧版JVM上具有非常小的固定大小,这导致即使不过度使用它也会发生大量冲突。除此之外,这意味着以线程安全的方式修改全局可见的数据结构,换句话说,这永远不可能是一项廉价的操作。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接