【问题标题】:String Concatenation Vs String Builder Append字符串连接与字符串生成器追加
【发布时间】:2010-12-30 16:30:21
【问题描述】:

所以...我有这种情况,我有一个 Foreach 循环,该循环遍历复选框列表以检查哪些被选中。对于每个选中的复选框,我必须进行相当长的字符串连接,包括 30 个平均长度为 20 个字符的不同字符串,然后将其作为 HTTP 请求发送出去。 2 个字符串取决于所选复选框的索引/值。

复选框列表的长度也是可变的,具体取决于用户的数据。我会说列表的平均长度是 20,但它可以达到 50-60。所以最坏的情况是执行整个字符串连接 60 次左右。

现在我正在通过“+”运算符进行简单的字符串连接,但我想知道使用 Stringbuilder 是否会更快。当然,这意味着我必须要么在循环中创建一个 Stringbuilder 对象,要么在循环之前创建它并在发出 HTTP 请求后在其末尾调用 Stringbuilder.Remove。

感谢任何人就这个问题分享的任何见解。

编辑
感谢大家的所有回复,所以从我收集的信息来看,我这样做的最佳方法是:

 StringBuilder sb = new StringBuilder();
 foreach (CheckBox item in FriendCheckboxList)
 {
     if (item.Checked)
     {
         sb.Append(string1);
         sb.Append(string2);
         sb.Append(string3);
         .
         .
         .
         sb.Append(stringLast);

         SendRequest(sb.ToString());
         sb.Length = 0;
      }
  }

【问题讨论】:

  • 我很抱歉,看来我对 Stringbuilder 的理解不够好。那么调用 Stringbuilder.ToString() 会清除 Stringbuilder?
  • 没有。将 Length 属性设置为零会清除它。
  • someStringBuilder.length = 0;
  • 你可以查看 stackoverflow.com/questions/1612797/…> SO post on Concatenation vs Stringbuilder perforamnce。
  • 我对您的问题投了赞成票,但我还想指出您的编辑具有误导性。您使用了“最好的方式”这个短语,它是 A) 主观的和 B) 不正确的。关于 A)更好的措辞将是“普遍同意的方式”。关于 B)在这种情况下“更好”的方法是使用 String.Concat 而不是 StringBuilder,如我提出的答案中所述。答案没有错,StringBuilder 很好。只是要小心声称某些东西是“最好的”,因为通常已经或将要发现实际上更好的东西。

标签: c#


【解决方案1】:

使用StringBuilder。这就是它的用途。

字符串是不可变的。字符串连接会创建一个新字符串,需要更多内存,并且通常被认为很慢:

string a = "John" + " " + "Saunders";

这将创建一个字符串“John”,然后创建另一个字符串“John Saunders”,最后将其分配给“a”。 “约翰”被留下用于垃圾收集。

string a = "John";
a += " ";
a += "Saunders";

这大致相同,因为“John”被替换为新字符串“John”,该字符串被新字符串“John Saunders”替换。原件留作垃圾收集。

另一方面,StringBuilder 被设计为附加、删除等。


例子:

StringBuilder sb = new StringBuilder();
for (int i=0; i<n; i++)
{
    sb.Length = 0;
    sb.Append(field1[i]);
    sb.Append(field2[i]);
    ...
    sb.Append(field30[i]);
    // Do something with sb.ToString();
}

【讨论】:

  • 如果我的问题看起来很无聊,我深表歉意,但我只是想知道是否在每个循环中创建一个新的 Stringbuilder 对象会导致不必要的开销。
  • 为什么要创建一个新的?
  • 我认为这意味着您建议在每个循环结束时调用 Stringbuilder.Remove 吗?
  • 约翰说真话 - ToString() 然后清除它,冲洗并重复。
  • @John:您的第一个示例实际上创建了 2 个字符串吗?我认为,编译器会将其优化为单个字符串,并且不会发生连接。
【解决方案2】:

多年来,这个话题一直被分析到死。最终结果是,如果您正在执行少量已知数量的连接,请使用“+”,否则使用 stringbuilder。根据您所说,与“+”连接应该更快。有大量(给予或接受)网站在分析这一点 - 谷歌搜索。

对于你所说的字符串的大小,无论如何都可以忽略不计。

编辑:再想一想,SB 可能更快。但就像我说的,谁在乎呢?

【讨论】:

  • 我不同意。如果他正在执行 30 次“+”操作,那么他正在分配和丢弃大量字符串,而不是附加到任何字符串。
  • 你在我编辑的时候进来了 :) 我认为 SB 可能会更快,但请记住,所有字符串的“丢弃”都是由 GC 处理的,它的工作效率很高,并没有真正由当前的执行路径处理。
  • 它仍然是不必要的 GC 工作,可以很容易地避免......它不像我们在谈论 2 或 3 个字符串......
  • 还有在丢弃之前的分配...我已经看到这些串联会导致明显的性能损失。
  • 和我一样,几年前我们使用库来完成我们所有的 XML 爱好 :) 尽管如此,虽然我不记得任何事情,但那里有图表显示这种规模的操作应该不是问题。 SB 可以做一些时髦的事情——我看到约翰你已经更新了你的帖子。我肯定会选择你的最终样品。不过,这并不是真正的问题。
【解决方案3】:

我知道这个问题已经得到解答,但我想指出,我实际上认为总是使用 StringBuilder 的“盲目接受为福音”的方法有时是错误的,这是一种情况。

有关背景,请参阅此博客条目:http://geekswithblogs.net/johnsperfblog/archive/2005/05/27/40777.aspx

简而言之,对于这种特殊情况,如上所述,通过避免使用 StringBuilder 并因此使用 + 运算符,您将看到更好的性能:

foreach (CheckBox item in FriendCheckboxList)
{
    if (item.Checked)
    {
        string request = string1 +
            string2 +
            string3 +
            .
            .
            .
           stringLast;
        SendRequest(request);
    }
}

原因是 C# 编译器(从 .NET 1.1 开始)会将该语句转换为对 String.Concat 的单个 IL 调用,并将字符串数组作为参数传递。该博客条目很好地概述了 String.Concat 的实现细节,但可以说,它对于这种情况非常有效。

【讨论】:

  • 感谢您提出这个问题。很多人认为他们正在保存 GC 调用,而实际上可能没有。如果它与性能相关并且它实际上可能很重要,那么就对其进行测量!很少有“总是使用 X”的规则是正确的。
  • 当有固定数量的字符串要连接时,string.Concat 会更好。它需要做的就是添加字符串长度的总和,分配内存,复制每个字符串,然后就完成了。如果您希望进行更多处理,StringBuilder 可能会更好。
【解决方案4】:

如果你问这个问题,你应该使用 StringBuilder 的原因有很多,但我会提供两个。

  1. 当您使用字符串连接时,它必须分配一个新缓冲区并将另一个字符串中的数据复制到新的字符串变量中。所以你会招致很多重复的分配。最终导致内存碎片化,占用堆空间,并为垃圾收集器做更多工作。 另一方面,StringBuilder 预先分配了一个缓冲区,并且当您向其中添加字符串时,不需要继续重新分配(假设初始缓冲区足够大)。这提高了性能并且对内存的负担要少得多。

  2. 作为开发人员,我们应该尝试预测未来的增长。假设您的列表随着时间的推移大幅增长,然后突然开始缓慢执行。如果您现在可以毫不费力地阻止这种情况,您为什么不这样做呢?

【讨论】:

    【解决方案5】:

    一般来说,我建议使用StringBuilder

    您是否对此进行了测试并检查了性能? 与重写代码需要多长时间相比,性能是否存在问题

    【讨论】:

    • 我已经完成了这个项目,所以现在我只是在寻找各种方法来优化我的代码以获得更好的性能。
    猜你喜欢
    • 2013-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 2016-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多