为什么有String时使用StringBuilder?


82

我是StringBuilder第一次接触,感到惊讶,因为Java已经具有一个非常强大的String类,可以追加。

为什么要上第二String堂课?

在哪里可以了解更多信息StringBuilder



我只是想指出一下,实际上这是我一次面试的问题。他们问我该如何填充一个大字符串
。..– preOtep

Answers:


171

String不允许附加。您在上调用的每个方法都会String创建一个新对象并返回它。这是因为String它是不可变的-它无法更改其内部状态。

另一方面StringBuilder是可变的。调用append(..)它时,它会更改内部char数组,而不是创建一个新的字符串对象。

因此,拥有:

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

而不是str += i,这将创建500个新的字符串对象。

请注意,在示例中,我使用了循环。正如helios在评论中指出的那样,编译器会自动将类似的表达式转换String d = a + b + c

String d = new StringBuilder(a).append(b).append(c).toString();

另请注意StringBuffer,除了StringBuilder。区别在于前者具有同步方法。如果将其用作局部变量,请使用StringBuilder。如果碰巧有可能被多个线程访问,请使用StringBuffer(这种情况较为罕见)


24
+1。您可以添加:“因此StrungBuilder会寻找性能”和“ Java编译器用新的StringBuilder(A).append(B).append(C).toString()替换A + B + C之类的表达式,以避免产生对象创建性能点球” :)
helios

非常感谢。你们都应得+1(将立即送达:)
2011年

超级像“不可变”对象的召回。
加里·

很好的回应。但是,我想念的是为什么我们不能让编译器弄清楚何时大部分时间只使用Stringbuilder ,包括in循环,这样您就不必将其视为开发人员了。:)
worldsayshi

好答案 。我想添加以下几行:字符串是不可变的,因为保存字符串的数组(即char []值)被声明为final,但是在StringBuilder的情况下,保存字符串的数组(即char []值)不是final。您可以在保存String的数组中进行更改(如果使用Stringbuilder的话)-Deen
John

60

这是一个具体的示例,说明为什么-

int total = 50000;
String s = ""; 
for (int i = 0; i < total; i++) { s += String.valueOf(i); } 
// 4828ms

StringBuilder sb = new StringBuilder(); 
for (int i = 0; i < total; i++) { sb.append(String.valueOf(i)); } 
// 4ms

如您所见,性能差异很大。


附言 我在Macbook Pro Dual核上运行了它。
阿米尔·拉明法

这并不能解释为什么
Steve Kuo

25
这确实解释了为什么有String时为什么使用StringBuilder?这并不能解释为什么StringBuilder这么快。但这不是问题。因此,这是一个有效的答案。
KeremBaydoğan2011年

2
@krmby-同意。回答为什么真的意味着另一个问题。
阿米尔·拉明法

12
我认为为使比较公正,您应该s = sb.ToString();在最后加上执行时间,因此至少在两个示例中您都做了相同的操作(结果是string)。
Scott Whitlock,2012年

19

字符串类是不可变的,而StringBuilder是可变的。

String s = "Hello";
s = s + "World";

上面的代码将创建两个对象,因为String是不可变的

StringBuilder sb = new StringBuilder("Hello");
sb.append("World");

上面的代码将只创建一个对象,因为StringBuilder不是不可变的。

课程:每当需要操纵/更新/附加String时,StringBuilder都会比String具有更高的效率。


8

StringBuilder用于构建字符串。具体而言,以非常高效的方式构建它们。String类在很多方面都有好处,但是当从较小的字符串部分组装一个新字符串时,它实际上具有非常糟糕的性能,因为每个新字符串都是一个全新的可重新分配的字符串。(这是不可变的)StringBuilder保留相同的序列并对其进行修改(mutable)。


5

StringBuilder类是可变的,与String不同,它允许您修改字符串的内容而无需创建更多String对象,当您大量修改字符串时,这可以提高性能。还有一个名为StringBuffer的StringBuilder副本,它也可以同步,因此非常适合多线程环境。

String的最大问题是您对其执行的任何操作都将始终返回一个新对象,例如:

String s1 = "something";
String s2 = "else";
String s3 = s1 + s2; // this is creating a new object.

4

当您处理较大的字符串时,StringBuilder很好。它可以帮助您提高性能。

这是我发现有帮助的文章

快速的Google搜索可能对您有所帮助。现在,您雇用了7个不同的人来为您进行Google搜索。:)


我们不是都在这里做无薪工作吗?
Deadpool

4

准确地说,将所有字符串相加的StringBuilder为O(N),而将字符串的相加为O(N ^ 2)。检查源代码,这是通过保留可变的char数组在内部实现的。StringBuilder使用数组长度复制技术来实现摊销O(N ^ 2)性能,但代价是可能需要将所需的内存加倍。您可以在结尾处调用trimToSize来解决此问题,但是通常StringBuilder对象只是临时使用。您可以通过提供有关最终字符串大小的良好开始猜测来进一步提高性能。


3

效率。

每次连接字符串时,都会创建一个新字符串。例如:

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

这将创建一个新的临时字符串,将“ a”和“ b”复制到其中以得到“ ab”。然后,它创建另一个新的临时字符串,将“ ab”和“ c”复制到其中,得到“ abc”。然后将此结果分配给out

结果是Schlemiel the Painter算法的O(n²)(二次)时间复杂度。

StringBuilder另一方面,可让您就地附加字符串,并根据需要调整输出字符串的大小。


许多JVM实现会将您的示例编译为StringBuilder,然后将最终结果转换为String。在这种情况下,它不会通过重复的String分配来组装。
斯科特,

1

Java具有String,StringBuffer和StringBuilder:

  • 字符串:不可变

  • StringBuffer:其可变和线程安全

  • StringBuilder:其可变但不是ThreadSafe,在Java 1.5中引入

字符串,例如:

public class T1 {

    public static void main(String[] args){

        String s = "Hello";

        for (int i=0;i<10;i++) {

            s = s+"a";
            System.out.println(s);
        }
    }
}

}

输出:将创建10个不同的字符串,而不仅仅是1个字符串。

Helloa
Helloaa
Helloaaa
Helloaaaa
Helloaaaaa
Helloaaaaaa
Helloaaaaaaa
Helloaaaaaaaa 
Helloaaaaaaaaa 
Helloaaaaaaaaaa

StringBuilder例如:仅将创建1个StringBuilder对象。

public class T1 {

    public static void main(String[] args){

        StringBuilder s = new StringBuilder("Hello");

        for (int i=0;i<10;i++) {    
            s.append("a");
            System.out.println(s);
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.