关于字符串连接行为
About string concatenation behaviours
我明白,鉴于字符串的不变性,
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
指令在这个时候做了什么?该行为是否与我的第一个示例相同?
只有 String
文字 或调用 intern()
方法的 Strings
被放入字符串池中。串联不会自动实习 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 literals(代码中引号中的字符串)是导致 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
复制构造函数几乎从未使用过,因为无论如何字符串都是不可变的。
有关详细信息,请阅读答案 here, here, and here。
让我们考虑你的第一个例子:
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[]
缓冲区(在你的情况下它不会被重新分配,因为所有附加不超过 [=18 的初始容量=]).所以它更快,因为你有更少的对象并且没有任何东西被复制多次。
写 a=new String("")
是没有用的,因为除了你已经有的空字符串(它是在你的 class 常量池加载期间创建和驻留的)之外,你还会有一个空字符串。除了额外的空字符串(在第一次循环迭代和垃圾收集后将不再使用)之外,它与第一种情况没有区别。
请注意,JLS 中并未明确指定如何实现字符串连接(使用隐式 StringBuilder
或其他技术),但通常 java 编译器使用 StringBuilder
对其进行翻译。
我明白,鉴于字符串的不变性,
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
指令在这个时候做了什么?该行为是否与我的第一个示例相同?
只有 String
文字 或调用 intern()
方法的 Strings
被放入字符串池中。串联不会自动实习 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 literals(代码中引号中的字符串)是导致 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
复制构造函数几乎从未使用过,因为无论如何字符串都是不可变的。
有关详细信息,请阅读答案 here, here, and here。
让我们考虑你的第一个例子:
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[]
缓冲区(在你的情况下它不会被重新分配,因为所有附加不超过 [=18 的初始容量=]).所以它更快,因为你有更少的对象并且没有任何东西被复制多次。
写 a=new String("")
是没有用的,因为除了你已经有的空字符串(它是在你的 class 常量池加载期间创建和驻留的)之外,你还会有一个空字符串。除了额外的空字符串(在第一次循环迭代和垃圾收集后将不再使用)之外,它与第一种情况没有区别。
请注意,JLS 中并未明确指定如何实现字符串连接(使用隐式 StringBuilder
或其他技术),但通常 java 编译器使用 StringBuilder
对其进行翻译。