为什么要在Java中使用StringBuffer而不是string连接运算符

有人告诉我,使用StringBuffer在Java中连接string比在String使用+运算符更高效。 当你这样做的时候会发生什么? 什么是StringBuffer做不同?

最好是使用StringBuilder(它是一个非同步的版本;什么时候并行地构buildstring?)这几天,几乎在所有情况下,但是这里会发生什么:

当你对两个string使用+时,它会像这样编译代码:

 String third = first + second; 

像这样的东西:

 StringBuilder builder = new StringBuilder( first ); builder.append( second ); third = builder.toString(); 

因此,只是一些小例子,通常没有什么区别。 但是,当你构build一个复杂的string时,你经常需要处理更多的事情, 例如,您可能会使用许多不同的附加语句,或者像这样的循环:

 for( String str : strings ) { out += str; } 

在这种情况下,每个迭代都需要一个新的StringBuilder实例和一个新的StringoutString的新值是不可变的)。 这非常浪费。 用一个StringBuilderreplace这个意味着你可以产生一个String而不是用你不关心的String填充堆。

对于简单的连接如:

 String s = "a" + "b" + "c"; 

使用StringBuffer是没有意义的 – 就像jodonnell指出的那样,它会被巧妙地翻译成:

 String s = new StringBuffer().append("a").append("b").append("c").toString(); 

但是在一个循环中连接string是非常不方便的,例如:

 String s = ""; for (int i = 0; i < 10; i++) { s = s + Integer.toString(i); } 

在这个循环中使用string将在内存中生成10个中间string对象:“0”,“01”,“012”等等。 在使用StringBuffer编写相同的代码时,只需更新StringBuffer一些内部缓冲区,而不创build不需要的那些中间string对象:

 StringBuffer sb = new StringBuffer(); for (int i = 0; i < 10; i++) { sb.append(i); } 

其实对于上面的例子,你应该使用StringBuilder (在Java 1.5中引入)而不是StringBufferStringBuffer稍微重一点,因为它的所有方法都是同步的。

一个不应该比另一个快。 这在Java 1.4.2之前是不正确的,因为当使用“+”运算符连接两个以上的string时,会在构build最终string的过程中创build中间的String对象。

但是,正如JavaDoc for StringBuffer所述,至less在Java 1.4.2中使用“+”运算符编译创build一个StringBuffer并向其中append()多个string。 所以没有区别,显然。

但是,在循环中使用向另一个string添加string时要小心! 例如:

 String myString = ""; for (String s : listOfStrings) { // Be careful! You're creating one intermediate String object // for every iteration on the list (this is costly!) myString += s; } 

但请记住,通常用“+”连接几个string比append()所有stringappend()在一起更清晰。

底下,它实际上创build并附加到一个StringBuffer,对结果调用toString()。 所以,你使用的其实并不重要。

所以

 String s = "a" + "b" + "c"; 

 String s = new StringBuffer().append("a").append("b").append("c").toString(); 

对于一个语句中的一系列内联附加,这是真的。 如果你在多个语句的过程中构build你的string,那么你正在浪费内存,一个StringBuffer或者StringBuilder是你最好的select。

我认为给定jdk1.5(或更高版本)和你的连接是线程安全的,你应该使用StringBuilder,而不是StringBuffer http://java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs-stringbuilder.html至于速度的收益:; http : //www.about280.com/stringtest.html

就个人而言,我会为可读性编写代码,所以除非您发现string连接会使您的代码变得相当慢,否则请使用任何方法使代码更具可读性。

在某些情况下,由于编译器进行优化,这已经过时了,但是一般的问题是这样的代码:

 string myString=""; for(int i=0;i<x;i++) { myString += "x"; } 

将如下所示(每一步都是下一次循环迭代):

  1. 构造长度为1的string对象,值“x”
  2. 创build一个大小为2的新string对象,将旧string“x”复制到它,在位置2添加“x”。
  3. 创build一个大小为3的新string对象,将旧string“xx”复制到其中,在位置3添加“x”。
  4. … 等等

正如你所看到的,每次迭代都需要复制一个字符,导致我们每个循环执行1 + 2 + 3 + 4 + 5 + … + N操作。 这是一个O(n ^ 2)操作。 但是如果我们事先知道我们只需要N个字符,我们可以在一个分配中完成,只有N个字符从我们正在使用的string中复制 – 仅仅是一个O(n)操作。

StringBuffer / StringBuilder避免了这一点,因为它们是可变的,所以不需要一遍又一遍地复制相同的数据(只要有空间复制到内部缓冲区中)。 他们避免执行一个分配,并按照其当前大小的一部分过度分配缓冲区的次数,按照与附加数量成正比的方式进行复制,从而得到O(1)的分期补偿。

不过值得注意的是,编译器经常会自动将代码优化成StringBuilder风格(或更好的 – 因为它可以执行常量折叠等)。

Java将string1 + string2转换为StringBuffer构造,append()和toString()。 这是有道理的。

但是,在Java 1.4及更早版本中,它将分别为语句中的每个 +运算符执行此操作。 这意味着做一个+ B + C将导致两个 StringBuffer结构与两个 toString()调用。 如果你有一连串的连接,它会变成一个真正的混乱。 自己做这意味着你可以控制这一点,并做到这一点。

Java 5.0和更高版本似乎更明智地做到这一点,所以它不是一个问题,而且确实不那么冗长。

AFAIK取决于JVM的版本,在1.5以前的版本中使用“+”或“+ =”实际上每次都复制整个string。

注意使用+ =实际上分配了string的新副本。

正如所指出的,在循环中使用+涉及复制。

当conactenated的string是编译时间常量那里在编译时连接,所以

 String foo = "a" + "b" + "c"; 

已编译为:

 String foo = "abc"; 

StringBuffer是可变的。 它将string的值添加到相同的对象,而不实例化另一个对象。 做的事情如:

 myString = myString + "XYZ" 

会创build一个新的 String对象。

要使用“+”连接两个string,需要为两个string分配一个新的string空间,然后从两个string中复制数据。 一个StringBuffer被优化为连接,并分配比最初需要更多的空间。 当连接一个新的string时,在大多数情况下,字符可以简单地复制到现有string缓冲区的末尾。
为了连接两个string,'+'操作符的开销可能会更less,但是当连接更多的string时,StringBuffer将会超前,使用更less的内存分配以及更less的数据复制。

StringBuffer类维护一个字符数组来保存你连接的string的内容,而+方法在每次被调用并附加两个参数(param1 + param2)时创build一个新的string。

StringBuffer更快,因为它可能能够使用其已经存在的数组来连接/存储所有的string。 2.即使它们不适合数组,它也会更快地分配一个更大的后备数组,然后为每个调用生成新的String对象。

由于string是不可变的,每个对+运算符的调用都会创build一个新的String对象,并将String数据复制到新的String中。 由于复制一个string需要时间与string的长度成线性关系,因此对+运算符的N次调用的结果为O(N 2 )运行时间(二次)。

相反,由于StringBuffer是可变的,所以每次执行Append()时都不需要复制String,所以N个Append()调用的序列需要O(N)个时间(线性)。 如果您将大量的string附加在一起,这只会在运行时产生重大影响。

如上所述,String对象是不可变的,也就是说一旦创build它(见下文)就不能改变。

String x = new String(“something”); // 要么

String x =“something”;

所以当你尝试连接String对象的时候,这些对象的值被取出并放入一个新的String对象中。

如果使用可变的StringBuffer,则可以不断地将值添加到char(原语)的内部列表中,该列表可以被扩展或截断以适应所需的值。 没有创build新的对象,只有在需要保存值时才会创build/删除新的字符。

在连接两个string时,实际上是在Java中创build第三个String对象。 使用StringBuffer(或者Java 5/6中的StringBuilder)会更快,因为它使用内部字符数组来存储string,并且当您使用其中一个add(…)方法时,它不会创build一个新的String目的。 相反,StringBuffer / Buider追加内部数组。

在简单的连接中,使用StringBuffer / Builder或“+”运算符连接string并不是一个真正的问题,但是当进行大量的string连接时,您会发现使用StringBuffer / Builder会更快。

更多信息:

StringBuffer是一个线程安全的类

 public final class StringBuffer extends AbstractStringBuilder implements Serializable, CharSequence { // .. skip .. public synchronized StringBuffer append(StringBuffer stringbuffer) { super.append(stringbuffer); return this; } // .. skip .. } 

但是StringBuilder不是线程安全的,因此如果可能的话,使用StringBuilder会更快

 public final class StringBuilder extends AbstractStringBuilder implements Serializable, CharSequence { // .. skip .. public StringBuilder append(String s) { super.append(s); return this; } // .. skip .. } 

因为string在Java中是不可变的,所以每当你连接一个string时,都会在内存中创build一个新的对象。 SpringBuffer在内存中使用相同的对象。

我认为最简单的答案是:速度更快。

如果你真的想知道所有这些内在的东西,你可以随时查看源代码:

http://www.sun.com/software/opensource/java/getinvolved.jsp

http://download.java.net/jdk6/latest/archive/

Java语言规范的string串联运算符+部分为您提供了更多关于+运算符如此慢的原因的背景信息。