18

最近我发现自己对所有大小的字符串连接都使用了 StringBuilder,但是在最近的一次性能测试中,我换掉了同事的stringOut = string1 + "。" 用于 StringBuilder 的string2样式连接(在 10000x + 循环中使用,每次都更新 StringBuilder)只是为了看看它在次要连接中会有什么不同。

我发现,在性能测试的多次运行中,无论连接或 StringBuilder (重申这是针对小连接),变化都微不足道地更高或更低。

StringBuilder 对象的“更新”在什么时候否定了使用它的好处?

4

7 回答 7

20

我遵循的规则是——

如果在编译时连接数未知,请使用 StringBuilder。

因此,在您的情况下,每个 StringBuilder 仅附加几次然后被丢弃。这和类似的东西不一样

string s = String.Empty;
for (int i = 0; i < 10000; ++i)
{
    s += "A";
}

使用 StringBuilder 会显着提高性能,因为否则您将不断分配新内存。

于 2009-02-15T11:01:39.787 回答
18

我确定我有另一个答案,我只发布了我的文章的链接,然后是它的摘要,但我们又来了。

  • 当你在一个非平凡的循环中连接时绝对使用StringBuilder- 特别是如果你不确定(在编译时)你将通过循环进行多少次迭代。例如,一次读取文件一个字符,使用 += 运算符构建一个字符串可能会导致性能自杀。

  • 当您可以(可读)指定需要在一个语句中连接的所有内容时,请务必使用连接运算符。(如果您有一系列要连接的东西,请考虑String.Concat显式调用 - 或者String.Join如果您需要分隔符。)

  • 不要害怕将文字分成几个连接的位 - 结果是一样的。例如,您可以通过将长文字分成几行来提高可读性,而不会损害性能。

  • 如果您需要连接的中间结果而不是提供下一次连接迭代,StringBuilder这对您没有帮助。例如,如果您从名字和姓氏构建一个全名,然后在末尾添加第三条信息(可能是昵称),则只有在StringBuilder不需要时才能使用(first name + last name) 用于其他目的的字符串(就像我们在创建Person对象的示例中所做的那样)。

  • 如果您只需要进行一些串联,并且您真的想在单独的语句中执行它们,那么您走哪条路并不重要。哪种方式更有效将取决于所涉及的字符串大小的连接数量以及它们连接的顺序。如果您真的认为那段代码是性能瓶颈,请以两种方式对其进行分析或基准测试。

于 2009-02-15T13:19:58.287 回答
6

有时值得查看文档

String 或 StringBuilder 对象的连接操作的性能取决于内存分配发生的频率。String 连接操作总是分配内存,而 StringBuilder 连接操作仅在 StringBuilder 对象缓冲区太小而无法容纳新数据时才分配内存。因此,如果串联固定数量的 String 对象,则 String 类更适合串联操作。在这种情况下,编译器甚至可以将各个连接操作组合成一个操作。如果串联任意数量的字符串,则 StringBuilder 对象更适合串联操作;例如,如果一个循环连接随机数量的用户输入字符串。

在您的示例中,每个输出字符串只有一个连接,因此 StringBuilder 不会为您带来任何好处。在您多次添加到同一个字符串的情况下,您应该使用 StringBuilder ,例如:

stringOut = ...
for(...)
    stringOut += "."
    stringOut += string2
于 2009-02-15T11:10:11.857 回答
2

我的经验法则很简单。

  1. 如果您可以合理地编写一个生成最终结果的表达式,则使用+.
  2. 如果不能(由于大小或可变性),请使用 StringBuilder。

根据我的经验,表达方式如下:

"Id: " + item.id + " name: " + item.name

可以比以下内容更容易编写和理解:

StringBuilder sb = new StringBuilder();
sb.append("Id: ").append(item.id);
sb.append(" name: ").append(item.name);

(然后使用sb上面的表达式编写的字符串),并且它的性能同样好(提示:查看编译后的代码以了解原因!)

另一方面,当需要随着时间(程序运行时)或空间(由来自代码不同部分的值组成)累积字符串时,以单行表达式不切实际的方式,然后 StringBuilder 避免了以下开销(时间和内存搅动):

String s = somethingExpression;
...
s += someOtherExpression;
...
s += yetAnotherExpression;
...
于 2009-02-15T13:22:01.870 回答
1

来自MSDN

[T] 如果串联固定数量的 String 对象,则 String 类更适合串联操作。在这种情况下,编译器甚至可以将各个连接操作组合成一个操作。如果串联任意数量的字符串,则 StringBuilder 对象更适合串联操作;例如,如果一个循环连接随机数量的用户输入字符串。

我想答案是“它取决于” - 如果你在一个循环中连接多个迭代,那么 StringBuilder 几乎总是会提供更好的性能,但唯一确定的方法是实际分析。

于 2009-02-15T11:11:53.790 回答
1

Coding Horror上有一篇有趣的文章。Jeff 在双核 3.5 GHz Core 2 Duo 上进行 100,000 次迭代得到以下结果:

 Simple Concatenation    - 606 ms
 String.Format           - 665 ms
 string.Concat           - 587 ms
 String.Replace          - 979 ms
 StringBuilder           - 588 ms
于 2009-02-15T11:15:37.650 回答
1

来自点网 Perls

何时使用 StringBuilder?

StringBuilder 完全是一种优化,除了内部实现之外,它对字符串 Concat 没有任何逻辑改进。也就是说,在您的高性能应用程序和网站中正确使用它至关重要。

有时,可以使用简单的字符串 Concats 使用 4 次或更少迭代的小循环。然而,在极端情况下,这可能是灾难性的。使用 StringBuilder 计划您的边缘情况。

于 2009-02-15T12:01:38.130 回答