StringBuilder与String Concat的比较

17
在我的项目中,我正在遍历一个数据视图结果。
 string html =string.empty;
 DataView dV = data.DefaultView;
 for(int i=0;i< dV.Count;i++)
 {
     DataRowView rv = dV[i];
     html += rv.Row["X"].Tostring();
 }

dV中的行数始终为3或4。

在这种情况下,使用字符串连接符 += 还是 StringBuilder 更好?为什么?


2
我认为在SO和Google上有很多关于这个的结果。 - Aristos
1
无论如何,我认为你不会获得显著的性能优势,所以请专注于可读性。 - V4Vendetta
1
@Aristos 在连接 1000 个字符串时很快,但只连接 3 个呢? - ie.
@Aristos 刚刚运行了这个示例 https://gist.github.com/2964397 - ie.
我不必运行它,我直接理解结果。在这里,字符串被转换为优化的String.Concat(a,b,c)——与循环中的+=不同。此外,创建字符串构建器和将其转换为字符串也会产生开销。 - Aristos
显示剩余5条评论
6个回答

41

我建议在这里使用 StringBuilder,因为它可以描述你正在做的事情。

如果只是简单地连接 3 或 4 个字符串,可能不会产生任何显着差异,甚至字符串连接 可能 会略微更快 - 但如果你的假设错误,并且有很多行,StringBuilder 将变得更加高效,而且它总是更能清楚地表述你的操作。

或者,可以使用类似下面的东西:

string html = string.Join("", dv.Cast<DataRowView>()
                                .Select(rv => rv.Row["X"]));

请注意,您目前在字符串之间没有任何分隔符。您确定这就是您想要的吗?(还要注意,您的代码当前并没有太多意义 - 在循环中您并没有使用i。为什么?)

我有一篇关于字符串连接的文章,其中更详细地讨论了为什么值得使用StringBuilder以及何时使用。

编辑:对于那些怀疑字符串连接速度更快的人,这里有一个测试 - 使用故意“恶劣”的数据,但只是为了证明它是可能的:

using System;
using System.Diagnostics;
using System.Text;

class Test
{
    static readonly string[] Bits = { 
        "small string",
        "string which is a bit longer",
        "stirng which is longer again to force yet another copy with any luck"
    };

    static readonly int ExpectedLength = string.Join("", Bits).Length;

    static void Main()        
    {
        Time(StringBuilderTest);
        Time(ConcatenateTest);
    }

    static void Time(Action action)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        // Make sure it's JITted
        action();
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 10000000; i++)
        {
            action();
        }
        sw.Stop();
        Console.WriteLine("{0}: {1} millis", action.Method.Name,
                          (long) sw.Elapsed.TotalMilliseconds);
    }

    static void ConcatenateTest()
    {
        string x = "";
        foreach (string bit in Bits)
        {
            x += bit;
        }
        // Force a validation to prevent dodgy optimizations
        if (x.Length != ExpectedLength)
        {
            throw new Exception("Eek!");
        }
    }

    static void StringBuilderTest()
    {
        StringBuilder builder = new StringBuilder();
        foreach (string bit in Bits)
        {
            builder.Append(bit);
        }
        string x = builder.ToString();
        // Force a validation to prevent dodgy optimizations
        if (x.Length != ExpectedLength)
        {
            throw new Exception("Eek!");
        }
    }
}

在我的计算机上运行的结果(使用/o+ /debug-编译):

StringBuilderTest: 2245 millis
ConcatenateTest: 989 millis

我已经运行了这个测试多次,包括反转测试顺序,结果是一致的。


2
@O.D:这不是一个固定的数字,而是在循环中。基本上,如果你可以用单个表达式表达所有内容,那么值得使用字符串连接。但这里并非如此。 - Jon Skeet
4
不 - 如果它在循环中,它就不是固定的。特别地,它不能被表示为单个表达式(因此也不能使用单个Concat调用)。 - Jon Skeet
2
@JonSkeet:作为昨天刚买了你的书的人,我不敢在C#问题上与您争论,但请看一下这个比较:http://www.codeproject.com/Articles/14936/StringBuilder-vs-String-Fast-String-Operations-wit - CloudyMarble
1
@Talha:如果涉及的字符串长度意味着StringBuilder在每次连接时都必须复制数据,则字符串连接可能会更快。首先需要一个StringBuilder对象,而调用ToString会进行额外的复制。请注意,在.NET 3.5和.NET 4之间,StringBuilder的实现发生了重大变化,但我仍然期望对于三到四个字符串使用字符串连接有时会更快。请注意,我并不推荐这样做。 - Jon Skeet
2
@Talha:你不需要这样做。你应该认识到,在循环中进行字符串拼接更快的情况下,差异几乎总是微不足道的 - 因此,你应该编写更干净的代码,同时也能够良好地扩展。 - Jon Skeet
显示剩余12条评论

5

建议使用StringBuilder..为什么不自己进行分析,然后决定哪个对您最好..

var stopWatch=new StopWatch();
stopWatch.Start();
string html =string.empty;
        DataView dV = data.DefaultView;
        for(int i=0;i< dV.Count;i++)
        {
           html += dV.Row["X"].Tostring();
        } 
stopWatch.Stop();
Console.Write(stopWatch.EllapsedMilliseconds());

var stopWatch=new StopWatch();
stopWatch.Start();
string html =new StringBuilder();
        DataView dV = data.DefaultView;
        for(int i=0;i< dV.Count;i++)
        {
           html.Append(dV.Row["X"].ToString());
        } 
var finalHtml=html.ToString();
stopWatch.Stop();
Console.Write(stopWatch.EllapsedMilliseconds());

1
我做了。两种方式都花费了1毫秒。 - Niraj Choubey
正如我想建议的那样,是的,在5-10次循环中,您不会看到任何差异,只需尝试循环1、5、10、15等,并查看经过的滴答数。 - bresleveloper
bresleveloper - 我知道如果有大量或随机数量的字符串需要连接,StringBuilder是高效的字符串连接方法。但是我想知道如果我们只有3或4个字符串需要连接,那么哪种方法更好。 - Niraj Choubey
3
你有任何证据表明这段代码的性能很重要吗?通常更好的选择是追求可读性 - 在这里,使用 StringBuilder 更清晰地表达了你的意图。 - Jon Skeet

2

文档中提到:

如果要连接的字符串数量是固定的,则首选使用String类进行连接操作。在这种情况下,编译器甚至可以将单个连接操作组合成一个操作。

如果要连接的字符串数量是任意的(例如,循环连接用户输入的随机数量的字符串),则首选使用StringBuilder对象。

因此,在您的情况下,我会说String更好。

编辑:

这是一个无休止的讨论,无论如何,我建议您检查平均操作次数并测试每个操作的性能以比较结果。

请参阅此链接,其中包括一些性能测试代码,以解决此问题。


1
我认为“...固定数量的字符串对象被连接…”的意思是像“abc”+“def”+“ghi”,而不是使用for循环。 - Betlista
如果我写一个for循环来连接3个字符串会有什么区别吗?没有,那么10个字符串呢?或者20个?循环不是问题所在,看看我的编辑,我认为原帖作者应该知道平均涉及多少操作,并测试每个选项在这些连接数量上的性能。 - CloudyMarble
可能您不知道编译器的工作原理。如果编译器在代码中看到"abc" + "def" + "ghi",它有可能将其合并为单个操作,就像您在帖子中所写的那样,但对于典型的循环,编译器无法知道循环次数,因此每个循环中都会有一次操作。这也是为什么循环展开是性能优化的原因。简单地说,“如果固定数量的字符串对象被连接”这个条件并不成立,因为即使您知道有3或4个项目,编译器也不知道。 - Betlista
但是在从0到99的循环中,我们知道涉及多少操作,而100是一个固定的数字,不是吗?我认为在这种情况下条件确实成立,它更取决于每个字符串的长度,而不是循环次数是否已知,stringbuilder可以节省内存,但并不总是更快的选项,C语言性能测试示例。 - CloudyMarble

1

StringBuilder 当然没问题。记住字符串是不可变的!

编辑:对于3-4行,像Jon Skeet在他的回答中所说的那样,连接将是更好的选择。


对于少量字符串,使用字符串拼接可能比 StringBuilder 更慢。特别是当 StringBuilder 需要在每次 Append 时扩展时,你仍然需要进行所有的复制开销,但是你还需要进行最终的复制操作和 StringBuilder 本身的开销。 - Jon Skeet
@JonSkeet,是的Jon,我刚看到问题中的“3-4行”部分。在编辑之前,它是代码的一部分,我错过了它。 - Habib

1

建议使用StringBuilder。它是可变的,对内存分配器的压力应该更小 :-)

字符串实例是不可变的。创建后无法更改。 任何看似更改字符串的操作都会返回一个新的实例。


0

你要找的是 StringBuilder。通常,如果有一个函数可以完成某项工作,请尽量利用它,而不是编写几乎相同的过程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接