有字符串时为啥要使用 StringBuilder?
Posted
技术标签:
【中文标题】有字符串时为啥要使用 StringBuilder?【英文标题】:Why StringBuilder when there is String?有字符串时为什么要使用 StringBuilder? 【发布时间】:2011-07-11 04:29:18 【问题描述】:我刚刚第一次遇到StringBuilder
,我很惊讶,因为Java 已经有一个非常强大的String
类允许追加。
为什么要第二个String
类?
我在哪里可以了解有关StringBuilder
的更多信息?
【问题讨论】:
另见:Exact difference between CharSequence and String in java 和 my nifty class diagram。 我只是想指出这实际上是我曾经提出的一个面试问题。他们问我会用什么来填充一个大字符串..... 【参考方案1】:String
不允许追加。您在 String
上调用的每个方法都会创建一个新对象并返回它。这是因为String
是不可变的 - 它无法更改其内部状态。
另一方面,StringBuilder
是可变的。当您调用 append(..)
时,它会更改内部 char 数组,而不是创建一个新的字符串对象。
因此更高效的是:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 500; i ++)
sb.append(i);
而不是str += i
,它将创建 500 个新的字符串对象。
请注意,在示例中我使用了循环。正如 helios 在 cmets 中指出的那样,编译器会自动将 String d = a + b + c
等表达式转换为类似
String d = new StringBuilder(a).append(b).append(c).toString();
还要注意,除了StringBuilder
,还有StringBuffer
。不同的是前者有同步方法。如果将其用作局部变量,请使用StringBuilder
。如果碰巧它可能被多个线程访问,请使用StringBuffer
(这种情况很少见)
【讨论】:
+1。您可以添加:“因此 StrungBuilder 寻找性能”和“Java 编译器将 A + B + C 等表达式替换为 new StringBuilder(A).append(B).append(C).toString() 以避免对象创建性能惩罚”:) 非常感谢。你们都应该得到 +1(会及时送达 :) 超级喜欢“不可变”对象的召回。 很好的回应。然而,我想念的是为什么我们不能让编译器确定何时只使用 Stringbuilder 大部分时间,包括在 for 循环中,这样你就不需要考虑它作为一个开发者。 :) 好答案。我想添加这些行:字符串是不可变的,因为保存字符串的数组(即 char [] 值)被声明为最终的,但在 StringBuilder 的情况下,保存字符串的数组(即 char [] 值)不是最终的。您可以在 Stringbuilder 的情况下更改保存字符串的数组【参考方案2】:这里有一个具体的例子说明为什么 -
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 core 上运行了这个。 这确实解释了 当有 String 时为什么要使用 StringBuilder? 这并不能解释 为什么 StringBuilder 这么快。 但这不是问题所在。所以这是一个有效的答案。 @krmby - 同意。回答为什么真的意味着另一个问题。 我认为为了公平比较,您应该在最后包括执行s = sb.ToString();
的时间,因此至少您在两个示例中都做了相同的操作(结果是string
) .【参考方案3】:
String 类是不可变的,而 StringBuilder 是可变的。
String s = "Hello";
s = s + "World";
上面的代码会创建两个对象,因为 String 是不可变的
StringBuilder sb = new StringBuilder("Hello");
sb.append("World");
上面的代码只会创建一个对象,因为 StringBuilder 不是不可变的。
经验:每当需要多次操作/更新/附加字符串时,请使用 StringBuilder,因为它比 String 更高效。
【讨论】:
【参考方案4】:StringBuilder 用于构建字符串。具体来说,以非常高效的方式构建它们。 String 类对很多事情都有好处,但实际上在将较小的字符串部分组装成一个新字符串时,它的性能非常糟糕,因为每个新字符串都是一个全新的、重新分配的字符串。 (它是 immutable)StringBuilder 在原地保持相同的序列并对其进行修改(mutable)。
【讨论】:
【参考方案5】:StringBuilder 类是可变的,与 String 不同,它允许您修改字符串的内容而无需创建更多的 String 对象,这在您大量修改字符串时可以提高性能。 StringBuilder 还有一个对应的 StringBuffer,它也是同步的,因此非常适合多线程环境。
String 最大的问题是你对它做的任何操作,总是会返回一个新对象,比如:
String s1 = "something";
String s2 = "else";
String s3 = s1 + s2; // this is creating a new object.
【讨论】:
【参考方案6】:当您处理较大的字符串时,StringBuilder 非常有用。它可以帮助您提高性能。
这是一个article,我发现它很有帮助。
快速的谷歌搜索可能会对您有所帮助。现在你雇佣了 7 个不同的人来为你做一个谷歌搜索。 :)
【讨论】:
我们不是都在这里做无薪工作吗? 提到的文章链接已关闭/损坏。【参考方案7】:确切地说,StringBuilder 添加所有字符串是 O(N),而添加 String 是 O(N^2)。检查源代码,这是通过保持一个可变的字符数组在内部实现的。 StringBuilder 使用数组长度复制技术来实现ammortized O(N^2) 性能,代价是可能需要的内存翻倍。你可以在最后调用 trimToSize 来解决这个问题,但通常 StringBuilder 对象只是临时使用。您可以通过对最终字符串大小提供良好的初始猜测来进一步提高性能。
【讨论】:
【参考方案8】:效率。
每次连接字符串时,都会创建一个新字符串。例如:
String out = "a" + "b" + "c";
这会创建一个新的临时字符串,将“a”和“b”复制到其中以生成“ab”。然后它创建另一个新的临时字符串,将“ab”和“c”复制到其中,从而产生“abc”。然后将此结果分配给out
。
结果是 O(n²)(二次)时间复杂度的 Schlemiel the Painter's algorithm。
另一方面,StringBuilder
允许您就地附加字符串,并根据需要调整输出字符串的大小。
【讨论】:
许多 JVM 实现会将您的示例编译为 StringBuilder,然后将最终结果转换为 String。在这种情况下,它不会通过重复的 String 分配来组装。【参考方案9】:Java 有 String、StringBuffer 和 StringBuilder:
字符串:不可变
StringBuffer:它的可变和线程安全
StringBuilder:它的可变但不是线程安全的,在 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);
【讨论】:
以上是关于有字符串时为啥要使用 StringBuilder?的主要内容,如果未能解决你的问题,请参考以下文章
既然我们已经有了 StringBuilder,为啥还要使用 StringJoiner?
为啥Java 8中不再需要StringBuilder拼接字符串
java中的StringBuilder有啥用?啥时候用StringBuilder?