什么时候使用StringBuilder?

发布于 2024-08-13 05:53:48 字数 137 浏览 7 评论 0原文

我了解了 StringBuilder 的好处。

但如果我想连接 2 个字符串,那么我认为不使用 StringBuilder 会更好(更快)。这是正确的吗?

在什么情况下(字符串数量)使用 StringBuilder 会变得更好?

I understand the benefits of StringBuilder.

But if I want to concatenate 2 strings, then I assume that it is better (faster) to do it without StringBuilder. Is this correct?

At what point (number of strings) does it become better to use StringBuilder?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

怕倦 2024-08-20 05:53:48

我强烈建议您阅读微优化剧场的悲惨悲剧,作者:杰夫·阿特伍德。

它处理简单连接与 StringBuilder 与其他方法。

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

I warmly suggest you to read The Sad Tragedy of Micro-Optimization Theater, by Jeff Atwood.

It treats Simple Concatenation vs. StringBuilder vs. other methods.

Now, if you want to see some numbers and graphs, follow the link ;)

紧拥背影 2024-08-20 05:53:48

但是如果我想连接2
字符串,那么我假设它是
更好(更快)地做到这一点,而不需要
字符串生成器。这是正确的吗?

这确实是正确的,您可以在以下位置找到很好的解释:

关于字符串和 StringBuilder 的文章

总结:如果您可以一次性连接字符串,那么

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 对象)。

  • 如果您只有一些串联
    去做,并且你真的很想做
    在单独的声明中,它没有
    你走哪条路真的很重要。哪个
    更有效的方式取决于
    串联数量 大小
    涉及的字符串以及顺序
    它们是串联起来的。如果你真的
    相信这段代码是
    性能瓶颈、配置文件或
    双向基准测试。

But if I want to concatinate 2
strings, then I assume that it is
better (faster) to do it without
StringBuilder. Is this correct?

That is indeed correct, you can find why exactly explained very well on :

Article about strings and StringBuilder

Summed up : if you can concatinate strings in one go like

var result = a + " " + b  + " " + c + ..

you are better off without StringBuilder for only on copy is made (the length of the resulting string is calculated beforehand.);

For structure like

var result = a;
result  += " ";
result  += b;
result  += " ";
result  += c;
..

new objects are created each time, so there you should consider StringBuilder.

At the end the article sums up these rules of thumb :

Rules Of Thumb

So, when should you use StringBuilder,
and when should you use the string
concatenation operators?

  • Definitely use StringBuilder when
    you're concatenating in a non-trivial
    loop - especially if you don't know
    for sure (at compile time) how many
    iterations you'll make through the
    loop. For example, reading a file a
    character at a time, building up a
    string as you go using the += operator
    is potentially performance suicide.
  • Definitely use the concatenation
    operator when you can (readably)
    specify everything which needs to be
    concatenated in one statement. (If you
    have an array of things to
    concatenate, consider calling
    String.Concat explicitly - or
    String.Join if you need a delimiter.)

  • Don't be afraid to break literals up
    into several concatenated bits - the
    result will be the same. You can aid
    readability by breaking a long literal
    into several lines, for instance, with
    no harm to performance.

  • If you need the intermediate results
    of the concatenation for something
    other than feeding the next iteration
    of concatenation, StringBuilder isn't
    going to help you. For instance, if
    you build up a full name from a first
    name and a last name, and then add a
    third piece of information (the
    nickname, maybe) to the end, you'll
    only benefit from using StringBuilder
    if you don't need the (first name +
    last name) string for other purpose
    (as we do in the example which creates
    a Person object).

  • If you just have a few concatenations
    to do, and you really want to do them
    in separate statements, it doesn't
    really matter which way you go. Which
    way is more efficient will depend on
    the number of concatenations the sizes
    of string involved, and what order
    they're concatenated in. If you really
    believe that piece of code to be a
    performance bottleneck, profile or
    benchmark it both ways.

站稳脚跟 2024-08-20 05:53:48

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

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

System.String is an immutable object - it means that whenever you modify its content it will allocate a new string and this takes time (and memory?).
Using StringBuilder you modify the actual content of the object without allocating a new one.

So use StringBuilder when you need to do many modifications on the string.

焚却相思 2024-08-20 05:53:48

并非如此...如果您连接字符串或者有很多连接(例如在循环中),则应该使用 StringBuilder。

Not really...you should use StringBuilder if you concatenate large strings or you have many concatenations, like in a loop.

空‖城人不在 2024-08-20 05:53:48

转述一下

然后你数到三,不能多也不能少。你要数的数应为三,所数的数应为三。你不能数四,也不能数二,除非你接着数三。一旦达到第三个数字,那就投掷你的安提阿圣手榴弹

我通常对任何代码块使用字符串生成器,这将导致三个或更多字符串的串联。

To paraphrase

Then shalt thou count to three, no more, no less. Three shall be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, neither count thou two, excepting that thou then proceed to three. Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch

I generally use string builder for any block of code which would result in the concatenation of three or more strings.

与酒说心事 2024-08-20 05:53:48
  • 如果您在循环中连接字符串,则应该考虑使用 StringBuilder 而不是常规 String
  • 如果是单个连接,您可能根本看不到执行时间的差异

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

static void Main(string[] args)
    {
        //warm-up rounds:
        Test(500);
        Test(500);

        //test rounds:
        Test(500);
        Test(1000);
        Test(10000);
        Test(50000);
        Test(100000);

        Console.ReadLine();
    }

    private static void Test(int iterations)
    {
        int testLength = iterations;
        Console.WriteLine($"----{iterations}----");

        //TEST 1 - String
        var startTime = DateTime.Now;
        var resultString = "test string";
        for (var i = 0; i < testLength; i++)
        {
            resultString += i.ToString();
        }
        Console.WriteLine($"STR: {(DateTime.Now - startTime).TotalMilliseconds}");



        //TEST 2 - StringBuilder
        startTime = DateTime.Now;
        var stringBuilder = new StringBuilder("test string");
        for (var i = 0; i < testLength; i++)
        {
            stringBuilder.Append(i.ToString());
        }
        string resultString2 = stringBuilder.ToString();
        Console.WriteLine($"StringBuilder: {(DateTime.Now - startTime).TotalMilliseconds}");


        Console.WriteLine("---------------");
        Console.WriteLine("");

    }

结果 (以毫秒为单位):

----500----
STR: 0.1254
StringBuilder: 0
---------------

----1000----
STR: 2.0232
StringBuilder: 0
---------------

----10000----
STR: 28.9963
StringBuilder: 0.9986
---------------

----50000----
STR: 1019.2592
StringBuilder: 4.0079
---------------

----100000----
STR: 11442.9467
StringBuilder: 10.0363
---------------
  • If you concatenate strings in a loop, you should consider using StringBuilder instead of regular String
  • In case it's single concatenation, you may not see the difference in execution time at all

Here is a simple test app to prove the point:

static void Main(string[] args)
    {
        //warm-up rounds:
        Test(500);
        Test(500);

        //test rounds:
        Test(500);
        Test(1000);
        Test(10000);
        Test(50000);
        Test(100000);

        Console.ReadLine();
    }

    private static void Test(int iterations)
    {
        int testLength = iterations;
        Console.WriteLine(
quot;----{iterations}----");

        //TEST 1 - String
        var startTime = DateTime.Now;
        var resultString = "test string";
        for (var i = 0; i < testLength; i++)
        {
            resultString += i.ToString();
        }
        Console.WriteLine(
quot;STR: {(DateTime.Now - startTime).TotalMilliseconds}");



        //TEST 2 - StringBuilder
        startTime = DateTime.Now;
        var stringBuilder = new StringBuilder("test string");
        for (var i = 0; i < testLength; i++)
        {
            stringBuilder.Append(i.ToString());
        }
        string resultString2 = stringBuilder.ToString();
        Console.WriteLine(
quot;StringBuilder: {(DateTime.Now - startTime).TotalMilliseconds}");


        Console.WriteLine("---------------");
        Console.WriteLine("");

    }

Results (in milliseconds):

----500----
STR: 0.1254
StringBuilder: 0
---------------

----1000----
STR: 2.0232
StringBuilder: 0
---------------

----10000----
STR: 28.9963
StringBuilder: 0.9986
---------------

----50000----
STR: 1019.2592
StringBuilder: 4.0079
---------------

----100000----
STR: 11442.9467
StringBuilder: 10.0363
---------------
給妳壹絲溫柔 2024-08-20 05:53:48

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

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

There's no definitive answer, only rules-of-thumb. My own personal rules go something like this:

  • If concatenating in a loop, always use a StringBuilder.
  • If the strings are large, always use a StringBuilder.
  • If the concatenation code is tidy and readable on the screen then it's probably ok.
    If it isn't, use a StringBuilder.
待"谢繁草 2024-08-20 05:53:48

由于很难找到一个既不受观点影响又不受骄傲之战影响的解释,因此我想在 LINQpad 上编写一些代码来自己测试一下。

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

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

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

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

迭代序列:2,3,4,5,6,7,8,9,10
迭代序列: 2, 3, 4, 5, 6, 7, 8, 9, 10

迭代顺序:10, 20, 30, 40, 50, 60, 70, 80
迭代序列: 10, 20, 30, 40, 50, 60, 70, 80

迭代序列:100, 200, 300, 400, 500
迭代序列: 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];
    }
}

Since it's difficult to find an explanation for this that's not either influenced by opinions or followed by a battle of prides I thought to write a bit of code on LINQpad to test this myself.

I found that using small sized strings rather than using i.ToString() changes response times (visible in small loops).

The test uses different sequences of iterations to keep time measurements in sensibly comparable ranges.

I'll copy the code at the end so you can try it yourself (results.Charts...Dump() won't work outside LINQPad).

Output (X-Axis: Number of iterations tested, Y-Axis: Time taken in ticks):

Iterations sequence: 2, 3, 4, 5, 6, 7, 8, 9, 10
Iterations sequence: 2, 3, 4, 5, 6, 7, 8, 9, 10

Iterations sequence: 10, 20, 30, 40, 50, 60, 70, 80
Iterations sequence: 10, 20, 30, 40, 50, 60, 70, 80

Iterations sequence: 100, 200, 300, 400, 500
Iterations sequence: 100, 200, 300, 400, 500

Code (Written using 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)
{
    
quot;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];
    }
}
听不够的曲调 2024-08-20 05:53:48

但是如果我想连接 2 个字符串,那么我认为不使用 StringBuilder 会更好更快。这是正确的吗?

是的。但更重要的是,在这种情况下使用普通的 String 可读性要高得多。另一方面,在循环中使用它是有意义的,并且也可以像串联一样具有可读性。

我会对引用特定连接数量作为阈值的经验规则持谨慎态度。在循环中(并且仅循环)使用它可能同样有用,更容易记住并且更有意义。

But if I want to concatenate 2 strings, then I assume that it's better and faster to do so without StringBuilder. Is this correct?

Yes. But more importantly, it is vastly more readable to use a vanilla String in such situations. Using it in a loop, on the other hand, makes sense and can also be as readable as concatenation.

I’d be wary of rules of thumb that cite specific numbers of concatenation as a threshold. Using it in loops (and loops only) is probably just as useful, easier to remember and makes more sense.

温柔一刀 2024-08-20 05:53:48

只要您可以实际键入串联数量(a + b + c ...),就不会有太大区别。 N 平方(N = 10)是 100 倍的减速,这应该不会太糟糕。

最大的问题是当您连接数百个字符串时。当 N=100 时,速度会减慢 10000 倍。这很糟糕。

As long as you can physically type the number of concatenations (a + b + c ...) it shouldn't make a big difference. N squared (at N = 10) is a 100X slowdown, which shouldn't be too bad.

The big problem is when you are concatenating hundreds of strings. At N=100, you get a 10000X times slowdown. Which is pretty bad.

情话已封尘 2024-08-20 05:53:48

单个串联不值得使用 StringBuilder。根据经验,我通常使用 5 个串联。

A single concatenation is not worth using a StringBuilder. I've typically used 5 concatenations as a rule of thumb.

饮湿 2024-08-20 05:53:48

我认为何时使用或何时不使用之间没有明确的界限。当然,除非有人进行了一些广泛的测试来得出黄金条件。

对我来说,如果只是连接 2 个巨大的字符串,我不会使用 StringBuilder。如果存在计数不确定的循环,我很可能会这样做,即使循环可能计数很小。

I don't think there's a fine line between when to use or when not to. Unless of course someone performed some extensive testings to come out with the golden conditions.

For me, I will not use StringBuilder if just concatenating 2 huge strings. If there's loop with an undeterministic count, I'm likely to, even if the loop might be small counts.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文