【问题标题】:How to use StringBuilder wisely?如何明智地使用 StringBuilder?
【发布时间】:2014-03-05 20:42:44
【问题描述】:

我对使用StringBuilder 类有点困惑,首先:

string 对象连接操作总是从现有的string 和新数据创建一个新对象。 StringBuilder 对象维护一个缓冲区以容纳新数据的连接。如果空间可用,则将新数据附加到缓冲区的末尾;否则,分配一个新的更大的缓冲区,将原始缓冲区中的数据复制到新缓冲区,然后将新数据附加到新缓冲区。

但是创建StringBuilder 实例以避免创建String 的新实例的意义何在?这听起来像是“一对一”交易。

static void Main(string[] args)
{
    String foo = "123";
    using (StringBuilder sb = new StringBuilder(foo)) // also sb isn't disposable, so there will be error
    {
        sb.Append("456");
        foo = sb.ToString();
    }

    Console.WriteLine(foo);
    Console.ReadKey();
}

为什么我不应该只使用

+=

编辑: 好的,我现在知道如何重用 StringBuilder 的一个实例(仍然不知道这是否符合代码标准),但这不值得只用一个string,不是吗?

【问题讨论】:

标签: c# .net string memory stringbuilder


【解决方案1】:

修改immutable 结构如strings 必须通过复制结构来完成,这样会消耗更多内存并减慢应用程序的运行时间(也会增加GC 时间等...)。

StringBuilder 通过使用相同的可变对象进行操作来解决这个问题。

但是:

在编译时连接 string 时如下:

string myString = "123";
myString += "234";
myString += "345";

它实际上会编译成这样的:

string myString = string.Concat("123", "234", "345");

这个函数比使用 StringBuilder 更快,因为知道进入函数的 strings 的数量。

所以对于编译时已知的string 连接,您应该更喜欢string.Concat()

对于未知数量的string,如以下情况:

string myString = "123";
if (Console.ReadLine() == "a")
{
    myString += "234";
}
myString += "345";

现在编译器不能使用 string.Concat() 函数,但是,StringBuilder 似乎只有在使用 6-7 个或更多 strings 进行连接时在时间和内存消耗方面更有效。

不良习惯用法:

StringBuilder myString = new StringBuilder("123");
myString.Append("234");
myString.Append("345");

精细练习用法(注意使用if):

StringBuilder myString = new StringBuilder("123");
if (Console.ReadLine() == "a")
{
    myString.Append("234");
}
myString.Append("345");

最佳实践用法(注意使用while循环):

StringBuilder myString = new StringBuilder("123");
while (Console.ReadLine() == "a")
{
    myString.Append("234"); //Average loop times 4~ or more
}
myString.Append("345");

【讨论】:

  • 你最后两个例子不是一模一样吗?
  • @JaimieKnox 请注意while/if 和评论。
  • 啊,谢谢。我一定已经读了 100 遍了,仍然错过了它。
  • 如果您在for 循环中连接列表中的每个项目怎么办? += 是否会编译为:string.concatenate(a,b,...,z)?如果是这样,如果列表中有大约 4 个或更多项目,您是否使用 StringBuilder
  • @monster 它不会被编译为string.Concat(一次在List 中的所有strings 之间),因为编译器不能假设任何关于stringsList 中,因此,如果您假设会有多个串联 - 应该使用StringBuilder
【解决方案2】:

string 是一个不可变类。不能修改,只能新建strings

因此,当您编写 result += a; 时,此时您的内存中有三个单独的 stringsaresult 的旧值和新值。当然,如果您只连接有限数量的strings,这绝对没问题。如果您在 for 循环中对大型集合进行迭代,则可能会出现问题。

StringBuilder 类在这些情况下提供了改进的性能。它没有创建新的strings 来存储连接的结果,而是使用相同的对象。所以如果你使用stringBuilder.Append(a);,你永远不会有“result的旧值”的等价物。


当然,这种内存效率是有代价的。当仅连接少量 stringsStringBuilder 时,在速度方面通常效率较低,因为与不可变的 string 类相比,它的开销更大。


要记住的一件事是,当您需要中间字符串时,StringBuilder 的效率可能会降低,因为在它上面调用.ToString() 会创建string 的新副本。

【讨论】:

    【解决方案3】:

    原因是因为strings 是不可变的。连接string 时,您会创建一个新的string。因此,当您需要连接多个strings 时,您会创建很多objects。就内存而言,这不会花费太多,因为每个string 只使用一次。但它确实为GC 提供了额外的工作。

    StringBuilder 但是每次都使用相同的object,但这样做是以牺牲易用性为代价的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      • 2023-04-11
      • 2020-04-22
      • 1970-01-01
      • 2018-04-25
      • 2013-10-31
      • 1970-01-01
      相关资源
      最近更新 更多