什么是Java中的字符串池?

Sub*_*hra 65 java

我对Java中的StringPool感到困惑.我在阅读Java中的String章节时遇到过这个问题.请以外行的方式帮助我理解StringPool实际上做了什么.

Nik*_*bak 121

这打印true(即使我们不使用equals方法:比较字符串的正确方法)

    String s = "a" + "bc";
    String t = "ab" + "c";
    System.out.println(s == t);
Run Code Online (Sandbox Code Playgroud)

当编译器优化您的字符串文字时,它会看到两者s并且t具有相同的值,因此您只需要一个字符串对象.它是安全的,因为String它在Java中是不可变的.
作为结果,双方st指向同一个对象,一些小的内存保存.

名称"字符串池"来自这样的想法,即所有已定义的字符串都存储在某个"池"中,并且在创建新的String对象编译器之前检查是否已定义此类字符串.

  • Java具有原始类型的包装类型,并且这些类是不可变的..如Integer,Charecter和Double ....等.他们还有一个池来节省内存吗?如果没有,有什么特别的String有一个池? (4认同)
  • @PunithRaj`String`与你给出的其他类型不是原始的,但通常被视为这样 - 所以它在java语言中相当"特殊".但是,java确实对包装类进行了类似的优化:[如果被装箱的值p为true,false,一个字节,或者在\ u0000到\ u007f范围内的字符,或者在-128到127之间的int或短数字(包括),然后让r1和r2成为p的任意两次拳击转换的结果.总是如此,r1 == r2.](http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.7)这些常见值是"汇集"很像`String`s. (4认同)
  • @PunithRaj我真的不确定!不过我对此表示怀疑.例如,int只有4个字节,所以你不要通过让两个Integer指向内存中的相同位置来节省那么多.相反,必须维护"整数池"以发现重复值可能会浪费更多内存,而不是通过避免重复值来节省内存. (2认同)
  • 很好的评论@PunithRaj,你应该把它作为一个单独的问题。 (2认同)

MSt*_*odd 36

我不认为它实际上做得太多,看起来它只是字符串文字的缓存.如果你有多个字符串的值是相同的,它们都将指向字符串池中相同的字符串文字.

String s1 = "Arul"; //case 1 
String s2 = "Arul"; //case 2 
Run Code Online (Sandbox Code Playgroud)

在案例1中,文字s1是新创建的并保存在池中.但在案例2中,文字s2引用s1,它不会创建新的.

if(s1 == s2) System.out.println("equal"); //Prints equal. 

String n1 = new String("Arul"); 
String n2 = new String("Arul"); 
if(n1 == n2) System.out.println("equal"); //No output.  
Run Code Online (Sandbox Code Playgroud)

http://p2p.wrox.com/java-espanol/29312-string-pooling.html


cHa*_*Hao 17

当JVM加载类或以其他方式查看文字字符串或某些代码intern字符串时,它会将字符串添加到一个隐藏最多的查找表中,该查找表具有每个此类字符串的一个副本.如果添加了另一个副本,则运行时会对其进行排列,以便所有文字引用相同的字符串对象.这被称为"实习".如果你说的话

String s = "test";
return (s == "test");
Run Code Online (Sandbox Code Playgroud)

它会返回true,因为第一个和第二个"测试"实际上是同一个对象.实习字符串这样比较可以了,比快String.equals,因为有一个单一的基准进行比较,而不是一堆char比较.

您可以通过调用将一个字符串添加到池中String.intern(),这将为您提供字符串的池化版本(可能是您正在实习的相同字符串,但您可能会疯狂地依赖它 - 您经常可以'确切地确定已经加载了什么代码并且直到现在运行并且实际上是相同的字符串).池化版本(从中返回的字符串intern)将等于任何相同的文字.例如:

String s1 = "test";
String s2 = new String("test");  // "new String" guarantees a different object

System.out.println(s1 == s2);  // should print "false"

s2 = s2.intern();
System.out.println(s1 == s2);  // should print "true"
Run Code Online (Sandbox Code Playgroud)


And*_*s_D 17

让我们从虚拟机规范的引用开始:

加载包含String文字的类或接口可能会创建一个新的String对象(第2.4.8节)来表示该文字.如果已经创建了一个String对象来表示该文字的上一个匹配项,或者如果已在表示与该文字相同的字符串的String对象上调用了String.intern方法,则可能不会发生这种情况.

这可能不会发生 - 这是一个暗示,String对象有一些特殊之处.通常,调用构造函数将始终创建该类的新实例.字符串不是这种情况,尤其是在使用文字"创建"String对象时.这些字符串存储在全局存储(池)中 - 或者至少引用保存在池中,并且每当需要已知字符串的新实例时,vm将从池中返回对该对象的引用.在伪代码中,它可能是这样的:

1: a := "one" 
   --> if(pool[hash("one")] == null)  // true
           pool[hash("one") --> "one"]
       return pool[hash("one")]

2: b := "one" 
  --> if(pool[hash("one")] == null)   // false, "one" already in pool
        pool[hash("one") --> "one"]
      return pool[hash("one")] 
Run Code Online (Sandbox Code Playgroud)

所以在这种情况下,变量ab持有对同一对象的引用.在这种情况下,我们有(a == b) && (a.equals(b)) == true.

如果我们使用构造函数,则不是这种情况:

1: a := "one"
2: b := new String("one")
Run Code Online (Sandbox Code Playgroud)

同样,"one"在池上创建,但后来我们从同一个文字创建一个新实例,在这种情况下,它导致(a == b) && (a.equals(b)) == false

那我们为什么要有一个字符串池呢?字符串,尤其是字符串文字广泛用于典型的Java代码中.而且他们是不变的.并且不可变允许缓存String以节省内存并提高性能(创建的工作量更少,收集的垃圾更少).

作为程序员,我们不必太在意字符串池,只要我们记住:

  • (a == b) && (a.equals(b))可能是truefalse(总是使用equals比较字符串)
  • 不要使用反射来更改char[]String 的后台(因为您不知道谁在使用该String实现)