什么时候使用StringBuilder?

时间:2009-12-01 12:08:38

标签: c# stringbuilder

我理解StringBuilder的好处。

但是如果我想连接2个字符串,那么我认为没有StringBuilder它会更好(更快)。这是对的吗?

在什么时候(字符串数量)使用StringBuilder会变得更好?

13 个答案:

答案 0 :(得分:69)

我热烈建议你阅读杰夫阿特伍德的The Sad Tragedy of Micro-Optimization Theater

它将Simple Concatenation与StringBuilder与其他方法相比较。

现在,如果您想查看一些数字和图表,请点击链接;)

答案 1 :(得分:38)

  

但如果我想要结合2   字符串,然后我认为它是   没有更好(更快)的做法   StringBuilder的。这是对的吗?

这确实是正确的,你可以找到为什么得到了很好的解释:

http://www.yoda.arachsys.com/csharp/stringbuilder.html

总结:如果你可以像

一样结合字符串
var result = a + " " + b  + " " + c + ..

没有StringBuilder你最好只在复制时(最终计算得到的字符串的长度。);

对于像

这样的结构
var result = a;
result  += " ";
result  += b;
result  += " ";
result  += c;
..

每次都会创建新对象,因此您应该考虑使用StringBuilder。

最后,文章总结了这些经验法则:

  

经验法则

     

那么,什么时候应该使用StringBuilder,   什么时候应该使用字符串   连接运算符?

     
      
  • 当使用StringBuilder时一定要使用   你是一个非平凡的联系人   循环 - 特别是如果你不知道   肯定(在编译时)有多少   迭代你将通过   环。例如,读取文件a   一次性格,建立一个   使用+ =运算符时的字符串   是潜在的表现自杀。

  •   
  • 绝对使用连接   操作员,你可以(可读)   指定需要的一切   在一个声明中连接起来。 (如果你   有一系列的东西   连接,考虑调用   String.Concat显式 - 或   String.Join如果你需要一个分隔符。)

  •   
  • 不要害怕打破文字   分成几个连接位 -   结果会一样。你可以帮忙   打破长文字的可读性   例如,用几行代表   没有损害表现。

  •   
  • 如果您需要中间结果   某事的串联   除了提供下一次迭代   串联,StringBuilder不是   去帮助你。例如,如果   你从第一个建立一个全名   名称和姓氏,然后添加一个   第三条信息(   昵称,也许)到最后,你会   只受益于使用StringBuilder   如果你不需要(名字+   姓氏)​​字符串用于其他目的   (就像我们在创建的例子中所做的那样   一个Person对象)。

  •   
  • 如果你只是有几个连接   要做,你真的想做   在单独的陈述中,它没有   你走哪条路真的很重要哪一个   方式更有效将取决于   大小的连接数   涉及的字符串,以及什么顺序   他们是连在一起的。如果你真的   相信这段代码是一个   性能瓶颈,简介或   以两种方式对它进行基准测试。

  •   

答案 2 :(得分:11)

System.String是一个不可变对象 - 这意味着无论何时修改其内容,它都会分配一个新字符串,这需要时间(和内存?)。 使用StringBuilder可以修改对象的实际内容,而无需分配新的内容。

因此,当您需要对字符串进行许多修改时,请使用StringBuilder。

答案 3 :(得分:8)

不是......你应该使用StringBuilder,如果你连接字符串,或者你有许多连接,就像在循环中一样。

答案 4 :(得分:4)

  

但是如果我想连接2个字符串,那么我认为没有StringBuilder这样做会更好更快。这是对的吗?

是。但更重要的是,在这种情况下使用香草String会更加可读。另一方面,在循环中使用它是有意义的,也可以像连接一样可读。

我会警惕将特定数量的连接作为阈值引用的经验法则。在循环中使用它(和仅循环)可能同样有用,更容易记忆并且更有意义。

答案 5 :(得分:4)

没有确定的答案,只有经验法则。我自己的个人规则是这样的:

  • 如果在循环中连接,请始终使用StringBuilder
  • 如果字符串很大,请始终使用StringBuilder
  • 如果连接代码在屏幕上整洁可读,那么它可能没问题 如果不是,请使用StringBuilder

答案 6 :(得分:4)

解释

  

然后你要数到三,不多,不少。三个应该是你要计算的数字,计数的数量应该是三个。四,你不算数,不计算你两个,除了你接着三个。一旦达到第三个数字,即为第三个数字,那么你就可以将你的圣手榴弹放在安提阿身上

我通常将字符串构建器用于任何代码块,这将导致三个或更多字符串的串联。

答案 7 :(得分:4)

  • 如果在循环中连接字符串,则应考虑使用StringBuilder而不是常规字符串
  • 如果它是单个连接,你可能根本看不到执行时间的差异

这是一个简单的测试应用程序来证明这一点:

class Program
{
    static void Main(string[] args)
    {
        const int testLength = 30000;
        var StartTime = DateTime.Now;

        //TEST 1 - String
        StartTime = DateTime.Now;
        String tString = "test string";
        for (int i = 0; i < testLength; i++)
        {
            tString += i.ToString();
        }
        Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
        //result: 2000 ms

        //TEST 2 - StringBuilder
        StartTime = DateTime.Now;
        StringBuilder tSB = new StringBuilder("test string");
        for (int i = 0; i < testLength; i++)
        {
            tSB.Append(i.ToString());
        }
        Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
        //result: 4 ms

        Console.ReadLine();
    }
}

<强>结果:

  • 30&#39; 000次迭代

    • 字符串 - 2000毫秒
    • StringBuilder - 4毫秒
  • 1000次迭代

    • 字符串 - 2毫秒
    • StringBuilder - 1 ms
  • 500次迭代

    • String - 0 ms
    • StringBuilder - 0 ms

答案 8 :(得分:3)

只要您可以在物理上输入连接数(a + b + c ...),它就不应该有很大的不同。 N平方(N = 10)是100倍减速,这不应该太糟糕。

最大的问题是当你连接数百个字符串时。在N = 100时,你会减速10000倍。这非常糟糕。

答案 9 :(得分:2)

我认为在何时使用或何时不使用之间存在细微差别。当然,除非有人进行了一些广泛的测试以证明黄金条件。

对我来说,如果只连接两个巨大的字符串,我将不会使用StringBuilder。如果存在具有不确定性计数的循环,我很可能,即使循环可能很小。

答案 10 :(得分:1)

单个串联不值得使用StringBuilder。我通常使用5个连接作为经验法则。

答案 11 :(得分:0)

StringBuilder 类为开发人员提供了一组方法,该方法允许操作可变的字符串,当您要修改字符串而不创建新对象时可以使用它,从而消除了普通String串联的开销或瓶颈问题。

在循环中将多个字符串连接在一起时,使用 StringBuilder 类可以提高性能。

下面是使用.NET中的StringBuilder类可以执行一些操作以操作字符串的操作的列表

附加:将信息附加到当前StringBuilder的末尾。

AppendFormat :用格式化文本替换字符串中传递的格式说明符。

插入:将字符串或对象插入当前StringBuilder的指定索引中。

删除:从当前的StringBuilder中删除指定数量的字符。

替换:在指定的索引处替换指定的字符。

答案 12 :(得分:0)

由于很难找到一个既不受观点影响又无自豪感的解释,我想在LINQpad上写一些代码来测试一下自己。

我发现使用小型字符串而不是使用i.ToString()会改变响应时间(在小循环中可见)。

该测试使用不同的迭代序列,以将时间测量值保持在合理的可比范围内。

我将在最后复制代码,以便您可以自己尝试(results.Charts ... Dump()在LINQPad之外不起作用)。

输出(X轴:测试的迭代次数,Y轴:以秒为单位的时间):

迭代顺序:2、3、4、5、6、7、8、9、10 Iterations sequence: 2, 3, 4, 5, 6, 7, 8, 9, 10

迭代顺序:10、20、30、40、50、60、70、80 Iterations sequence: 10, 20, 30, 40, 50, 60, 70, 80

迭代顺序:100、200、300、400、500 Iterations sequence: 100, 200, 300, 400, 500

代码(使用LINQPad 5编写):

void Main()
{
    Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
    Test(10, 20, 30, 40, 50, 60, 70, 80);
    Test(100, 200, 300, 400, 500);
}

void Test(params int[] iterationsCounts)
{
    $"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();

    int testStringLength = 10;
    RandomStringGenerator.Setup(testStringLength);
    var sw = new System.Diagnostics.Stopwatch();
    var results = new Dictionary<int, TimeSpan[]>();

    // This call before starting to measure time removes initial overhead from first measurement
    RandomStringGenerator.GetRandomString(); 

    foreach (var iterationsCount in iterationsCounts)
    {
        TimeSpan elapsedForString, elapsedForSb;

        // string
        sw.Restart();
        var str = string.Empty;

        for (int i = 0; i < iterationsCount; i++)
        {
            str += RandomStringGenerator.GetRandomString();
        }

        sw.Stop();
        elapsedForString = sw.Elapsed;


        // string builder
        sw.Restart();
        var sb = new StringBuilder(string.Empty);

        for (int i = 0; i < iterationsCount; i++)
        {
            sb.Append(RandomStringGenerator.GetRandomString());
        }

        sw.Stop();
        elapsedForSb = sw.Elapsed;

        results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
    }


    // Results
    results.Chart(r => r.Key)
    .AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
    .AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
    .DumpInline();
}

static class RandomStringGenerator
{
    static Random r;
    static string[] strings;

    public static void Setup(int testStringLength)
    {
        r = new Random(DateTime.Now.Millisecond);

        strings = new string[10];
        for (int i = 0; i < strings.Length; i++)
        {
            strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
        }
    }

    public static string GetRandomString()
    {
        var indx = r.Next(0, strings.Length);
        return strings[indx];
    }
}