2011-02-21 42 views
6

有两个问题。字符串实习和字面字符串声明的搜索开销

  1. 当我们声明字面字符串时,我们搜索堆的字符串池中是否有相同的字符串。 这是否也是实习生(方法实习生String)?

  2. 在我的思想中,每个文字字符串声明需要一个二进制搜索或东西,所以它的成本至少的log(n)ñ是池中现有弦数。如果池中有很多字符串,可能会造成很高的成本。 (也许权衡搜索成本和内存?)从这个角度来看,声明字符串可能是危险的。 这个搜索成本有多重要,以及为什么用这种方式设计java(当声明字面字符串时搜索池)。

以下是我参考了解的背景。


JavaDoc for the java.lang.String class状态:

字符串是常数;它们的值在创建后无法更改。字符串缓冲区支持可变字符串。因为String对象是不可变的,所以它们可以共享。

http://www.janeg.ca/scjp/lang/strLiteral.html评论:

换句话说,因为编译器知道字符串原始值不能改变,一旦它创造了它可以安全地使用现有数据和避免重复搞乱内存。

+0

我将您对“JSK 1.3”的引用修改为官方的JavaDoc。 –

+0

@joachim Sauer谢谢,但最后一句来自你删除的(http://www.janeg.ca/scjp/lang/strLiteral.html)。你能反映一下吗?或者我会的。 – RENO

+0

我删除了它,因为上面链接的JavaDoc是引用的授权原始源,并且该页的质量有问题(没有“JSK 1.3”这样的东西,并且它实际上并没有链接到它的任何源) 。 –

回答

4

您将编译时间复杂性与运行时复杂性混为一谈。

当这个类被加载时,是的,它做了一个搜索,看看每个文字是否已经存在(尽管我认为它会使用散列表进行O(1)查找而不是提议)。

代码运行时,它具有对内存中字符串的引用,因此没有额外的开销而非非文字。

所以是的,文字是interned。根据字符串Javadoc,

字符串池,最初是空的,由类String私人维护。

您可以在字符串上调用intern()将其添加到此池中。从逻辑上看,如果a.equals(b)然后a.intern() == b.intern(),因为.intern()保证返回一个独特的池。

例子:

class InternTest { 
    // assuming InternTest is the only class, internPool.size = 0 
    String x = "ABC"; // interned at class load, internPool.size = 1 
    String y = "DEF"; // interned at class load, internPool.size = 2 
    String z = "ABC"; // interned at class load, but match found - size = 2 still 

    void foo() { 
     // random int is just a mechanism to get something that I know won't 
     // be interned at loadtime - could have loaded from file or database too 
     int i = (new java.util.Random()).nextInt(1000) + 100; 
     int j = i; 
     String s = String.valueOf(i); // not yet interned, size = 2 still 
     String t = String.valueOf(j); // not yet interned, size = 2 still 

     String sIntern = s.intern(); // manually interned, size = 3 now 
     String tIntern = t.intern(); // manually interned, match found, size = 3 still 

     System.out.println("equals: " + (s.equals(t))); // should be true 
     System.out.println("== raw: " + (s == t)); // should be false, different variables 
     System.out.println("== int: " + (sIntern == tIntern)); // should be true, from unique pool 

     System.out.println("x and z: " + (x == z)); // should be true, interned at class load 
    } 

    public static void main(String[] args) { 
     (new InternTest()).foo(); 
    } 

} 

结果时运行:

C:\Documents and Settings\glowcoder\My Documents>java InternTest 
equals: true 
== raw: false 
== int: true 
x and z: true 

我要指出的是,假设永远是正确的。 Java语言本身有很多String s,在我们的String有机会看到白天之光之前会被实施。然而,假设一切都按顺序加载,如果你只考虑字符串的增量,并且假设没有与现有实习生发生冲突(我们都知道实习生可能很挑剔并且充满戏剧性,对吧?snicker),那么这些数字确实指示字符串池的大小的增量。

+2

实际上,String interning *不会*在运行时发生(当类被加载时)。但是它只发生一次字符串并且复杂度*是*'O(1)',所以它不是性能问题。 –

+0

当我想到它时,这是有道理的 - 没有JVM加载,我们将如何保持HashMap的内容?此外,因为'intern()'是一个本地方法,所以它不能在编译时完成。我会相应地更新我的答案。谢谢! – corsiKa

+0

感谢您快速回答!我得到了一些进一步的问题。在你的回答中,_一个独特的pool_,哪一个是唯一的手段:1)池的每个元素是唯一的2)池是唯一的。如果在编译时有2个元素,然后在运行时声明第三个字符串没有实际运行,那么第三个字符串也是在同一个池中? – RENO

3

1 - 当我们声明字面字符串时,我们搜索堆的字符串池中是否有相同的字符串。这也是一个实习(String类的方法实习生)?

是的。这个过程被称为实习。然而,它发生只是一次 ...当包含文字的类被加载。

2 - 在我看来,每个文字字符串声明都需要一个二进制搜索或其他东西,所以当n是池中现有字符串的数量时,它至少需要log(n)。

不,它不。该池是一个哈希表。

...并且如果池中有很多串,它可能是高成本的。

不,它不会。查询字符串池散列表的成本是O(1)

...从这个角度来看,声明很多文字字符串可能是危险的。

与其他加载成本相比,成本并不显着,然后JIT编译类文件。在声明大量文字字符串时,没有与性能相关的“危险”。

很明显,与字符串文字对应的String对象占用内存“永久”,并且您通常不希望浪费内存不必要。但是如果你需要使用这些常量字符串,它们必须以某种方式表示。而其他表示方式要么以其他方式使用内存,要么涉及其他运行时成本;例如从文件中读取它们或从数据库中检索它们的成本。

实习字符串文字的好处是,堆不会与相同文字字符串的多个副本混杂在一起。这对于典型的SE/EE应用来说可能并不重要,但是对于ME平台堆内存来说是非常重要的,浪费它会是一件坏事。


@RENO询问字符串被实现的次数。有两种情况:

  • String.intern()显式调用的应用程序选择,使发生,因为许多(或尽可能少)次。

  • 对于字符串文字,javac编译器将确保给定的.class文件在其常量池中不包含任何字符串文字的多个副本。这意味着一个在许多地方具有给定文字的类只会导致文字在加载类时被执行一次。但是,如果您有两个在其各自的源代码中具有相同文字字符串的类,则它们都将在其各自的常量池中具有字符串值,并且在加载相应的类时都会在字符串中进行实习。

+0

感谢您的好回答!你能否解释更多关于你的解释:_是的。这个过程被称为实习。但是,它只发生一次......当包含文字的类被加载时._我认为(实际数量)与(文字字符串的数量+明确的intern()方法的数量)相同。 – RENO

+0

再次感谢!当我读到你的解释时,我发现与[Java语言规范](http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#101084),3.10.5例。在链接页面中,(Other.hello == hello)的结果为true。我无法得到它,因为你解释说:_如果你有两个类与..._,但他们的结果是一样的。有没有我错过的一点? – RENO

+0

@RENO - 我不明白你的困惑。正如JLS解释的那样,每个字符串文字都是被实施的。期。我刚刚回答了关于需要调用intern()来实现这一点的**次数**的问题。 –