it-swarm.cn

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

有人告诉我,使用StringBuffer连接Java中的字符串比使用+运算符来使用Strings更有效。当你这样做时会发生什么? StringBuffer做什么不同?

53
harry

最好使用StringBuilder(它是一个不同步的版本;你什么时候并行构建字符串?)这几天,几乎在所有情况下,但这是发生的事情:

当你使用+两个字符串时,它会编译如下代码:

String third = first + second;

对于这样的事情:

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

因此,仅举几个例子,通常没有什么区别。但是当你构建一个复杂的字符串时,你经常需要处理的事情比这更多;例如,您可能正在使用许多不同的附加语句,或者像这样的循环:

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

在这种情况下,每次迭代都需要一个新的StringBuilder实例和一个新的Stringout的新值 - Strings是不可变的)。这非常浪费。用单个StringBuilder替换它意味着你只能产生一个String而不用你不关心的String来填充堆。

61
Calum

对于简单的连接,例如:

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

使用StringBuffer是毫无意义的 - 因为 jodonnell 指出它将被巧妙地翻译成:

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

_但是_ 在循环中连接字符串是非常不合适的,例如:

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

在此循环中使用string将在内存中生成10个中间字符串对象:“0”,“01”,“012”等等。使用StringBuffer编写相同内容时,只需更新StringBuffer的一些内部缓冲区,并且不创建不需要的中间字符串对象:

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

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

42
tkokoszka

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

但是,作为StringBuffer的 JavaDoc 表示,至少自从使用“+”运算符的Java 1.4.2编译为向其创建StringBufferappend()之后的许多字符串。所以没有区别,显然。

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

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;
}

但请记住,通常用“+”连接几个字符串比append()它们更清晰。

20
André Chalella

在引擎盖下,它实际上创建并附加到StringBuffer,在结果上调用toString()。所以你使用它实际上并不重要。

所以

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

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

对于在单个语句中的一堆内联追加,这是正确的。如果你在多个语句的过程中构建你的字符串,那么你就是在浪费内存,而StringBuffer或StringBuilder是你更好的选择。

9
jodonnell

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

就个人而言,我的代码是为了便于阅读,所以除非你发现字符串连接使你的代码相当慢,否则请使用哪种方法使代码更具可读性。

7
slipset

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

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

将按以下方式行事(每一步都是下一个循环迭代):

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

如您所见,每次迭代都需要复制一个字符,导致我们在每个循环中执行1 + 2 + 3 + 4 + 5 + ... + N个操作。这是O(n ^ 2)操作。但是,如果我们事先知道我们只需要N个字符,我们可以在一次分配中完成,只使用我们使用的字符串中的N个字符的副本 - 仅仅是O(n)操作。

StringBuffer/StringBuilder避免这种情况,因为它们是可变的,因此不需要反复复制相同的数据(只要有空间可以复制到它们的内部缓冲区中)。它们避免执行分配和复制,与通过按当前大小的一部分过度分配缓冲区所做的附加次数成比例,给予摊销O(1)追加。

然而值得注意的是,编译器通常会自动将代码优化为StringBuilder样式(或者更好 - 因为它可以执行常量折叠等)。

5
Brian

AFAIK它依赖于JVM的版本,在1.5之前的版本中使用“+”或“+ =”实际上每次复制整个字符串。

注意使用+ =实际分配新的字符串副本。

正如指向使用+ in循环涉及复制。

当连接的字符串是编译时常量时,它们在编译时连接起来,所以

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

已编译为:

String foo = "abc"; 
3
jb.

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

但是,在Java 1.4及更早版本中,它将在语句单独中为each +运算符执行此操作。这意味着执行a + b + c将导致two St​​ringBuffer构造与two toString()调用。如果你有一长串的concats,它将变成一个真正的混乱。自己动手意味着你可以控制它并正确地做到这一点。

Java 5.0及更高版本似乎更明智地做到了,所以它不是一个问题,而且肯定不那么冗长。

3
Alan Krueger

更多的信息:

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 ..
}

1
Eric Yung

StringBuffer是可变的。它将字符串的值添加到 same object而不实例化另一个对象。做类似的事情:

myString = myString + "XYZ"

将创建一个 new String对象。

1
Loren Segal

要使用'+'连接两个字符串,需要为两个字符串分配一个新字符串,然后从两个字符串复制数据。 StringBuffer针对连接进行了优化,并且最初分配的空间超出了所需的空间。连接新字符串时,在大多数情况下,可以简单地将字符复制到现有字符串缓冲区的末尾。
为了连接两个字符串,'+'运算符可能会有更少的开销,但是当你连接更多的字符串时,StringBuffer将提前出现,使用更少的内存分配,减少数据复制。

1
Eclipse

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

StringBuffer更快,因为1.它可能能够使用其现有的数组来连接/存储所有字符串。 2.即使它们不适合数组,也可以更快地分配更大的后备数组,然后为每次调用生成新的String对象。

1
Matt Novinger

因为字符串是不可变的,所以每次调用+运算符都会创建一个新的String对象,并将String数据复制到新的String。由于复制String在String的长度上需要线性时间,因此对+运算符的N个调用序列将导致O(N2)运行时间(二次)。

相反,由于StringBuffer是可变的,因此每次执行Append()时都不需要复制String,因此N Append()调用序列需要O(N) time(线性)。如果要将大量字符串附加在一起,这只会在运行时产生显着差异。

1
Adam Rosenfield

如上所述,String对象是不可变的,这意味着一旦创建它(见下文)就无法更改。

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

字符串x =“某事”;

因此,当您尝试连接String对象时,将获取这些对象的值并将其放入新的String对象中。

如果您改为使用IS可变的StringBuffer,则不断将值添加到char(基元)的内部列表中,可以对其进行扩展或截断以适合所需的值。不会创建新对象,只有在需要保存值时才会创建/删除新的char。

1
Christian P.

连接两个字符串时,实际上是在Java中创建第三个String对象。使用StringBuffer(或Java 5/6中的StringBuilder)更快,因为它使用内部字符数组来存储字符串,当你使用其中一个add(...)方法时,它不会创建新的字符串宾语。相反,StringBuffer/Buider附加内部数组。

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

1
Alexandre Brasil

因为字符串在Java中是不可变的,所以每次连接一个String时,都会在内存中创建新对象。 StringBuffer在内存中使用相同的对象。

0
Ivan Bosnic

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

如果你真的想知道所有引擎盖下的内容,你可以随时查看源代码:

http://www.Sun.com/software/opensource/Java/getinvolved.jsp

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

0
rgcb

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

0
Benedikt Waldvogel