4

我知道,鉴于字符串的不变性,类似于

String a="";
for(int i=0;i++<9;)
    a+=i;

效率非常低,因为最初实例化一个字符串并将其放入字符串池中,然后a+=i创建一个新字符串(第一个循环中的0),由 引用a并且前一个字符串现在可以进行垃圾回收。这发生了九次。

更好的方法是使用StringBuilder

StringBuilder a=new StringBuilder("");
for(int i=0;i++<9;)
    a.append(i);

new但是当我用关键字实例化字符串时,幕后会发生什么 ?

String a=new String("");
for(int i=0;i++<9;)
    a+=i;

我知道在这种情况下a不会被实习(它不在字符串池中),但它仍然是不可变的吗?a+=i这段时间指令做什么?行为与我的第一个示例相同吗?

4

2 回答 2

4

只有String literals,或者Strings您调用intern()方法 on 被放入字符串池中。连接不会自动实习 a String,因此您的示例将与字符串池相同。

String abc = new String("abc"); //"abc" is put on the pool
abc += "def"; //"def" is put on the pool, but "abcdef" is not
String xyz = "abcdefghi".substring(0, 6).intern(); //"abcdef" is now added to the pool and returned by the intern() function
String xyz = "test"; //test is put on the pool
xyz += "ing"; //ing is put on the pool, but "testing" is not

为了对此进行扩展,请注意 String 构造函数不会自动实习(或实习) String 。字符串文字(代码中引号中的字符串)的使用是导致字符串出现在字符串池中的原因。

String abc = "abc"; //"abc" is in the pool
String def = "def"; //"def" is in the pool
String str1 = new String(abc + def); //"abcdef" is not in the pool yet
String str2 = new String("abcdef"); //"abcdef" is on the pool now

另请注意,String几乎从不使用复制构造函数,因为字符串无论如何都是不可变的。

有关更多信息,请阅读此处此处此处的答案。

于 2015-06-01T14:12:29.817 回答
3

让我们考虑您的第一个示例:

String a="";
for(int i=0;i++<9;)
    a+=i;

这段代码将像这样执行:

String a="";
a=new StringBuilder(a).append(0).toString();
a=new StringBuilder(a).append(1).toString();
a=new StringBuilder(a).append(2).toString();
a=new StringBuilder(a).append(3).toString();
a=new StringBuilder(a).append(4).toString();
a=new StringBuilder(a).append(5).toString();
a=new StringBuilder(a).append(6).toString();
a=new StringBuilder(a).append(7).toString();
a=new StringBuilder(a).append(8).toString();
a=new StringBuilder(a).append(9).toString();

因此,对于每个循环迭代,您都会StringBuilder从字符串中创建一个新的(每次分配一个新的内部缓冲区),每次都char[]将其转换回。String另一方面,在第二种情况下,您将有效地拥有

StringBuilder a=new StringBuilder("").append(0).append(1).append(2).append(3)
    .append(4).append(5).append(6).append(7).append(8).append(9);

因此,您将只有一个StringBuilder只有一个内部char[]缓冲区(在您的情况下它不会被重新分配,因为所有附件都不会超过 的初始容量16)。所以它更快,因为你有更少的对象并且没有任何东西被复制多次。

写入a=new String("")是无用的,因为除了已经拥有的空字符串(在加载类常量池期间创建和实习)之外,您还将拥有一个空字符串。除了额外的空字符串(在第一次循环迭代和垃圾收集后将变得未使用)之外,它与第一种情况没有什么不同。

请注意,在 JLS 中没有明确指定如何实现字符串连接(使用隐式StringBuilder或其他技术),但通常 java 编译器使用StringBuilder.

于 2015-06-01T14:23:51.783 回答