C#: 字符串连接 (+=) 与 StringBuilder 效率分析

在 C# 或 Java 中,我们可以使用诸如 StringBuilder, StringBuffer 等方式对大量字符串进行拼接操作。当然,我们也可以直接使用 字符串连接 (+=) 的方式进行拼接操作。但是,两种主要方式在效率上有何区别呢?我们是否需要针对这两类方式进行区别场景的使用呢?针对于此,我做了如下测试,以证明两种不同方式连接字符串的效率临界值。(关于Java中StringBuilder和StringBuffer的区别,可以参阅这篇文章

测试方法是:重复连接字符串 “Hello World!” 1次到40次,在每次连接的过程中都分别采用 String Connection (+=) 和 StringBuilder 的 Append()方法进行1000次,并分别计算出两种方法在这1000次运算过程中的获胜比率。通过多次试验,得到如下统计:

第一次:
StringBuilder Efficiency Test 1

第二次:
StringBuilder Efficiency Test 2

第三次:
StringBuilder Efficiency Test 3

多次测试结果显示,在少于8~9次字符串连接时,String Connection (+=) 的效率要略胜一筹。这个结果与 Alois Kraus 给出的测试结果基本一致。

So, 该啥时候使用 +=,想必大家可以了解了。在叠加次数已知的前提下,若少于10次,可以优先考虑 +=,否则请不要犹豫的使用StringBuilder。

* 这里需要注意的是,StringBuilder 的默认 Capacity 是 16,当然,测试结果还没到这个数值,所以不会在较大程度上受此影响。
internal const int DefaultCapacity = 16;

相关测试代码如下:

namespace StringEfficiencyTest
{
    #region using directives

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

    #endregion using directives

    internal class Program
    {
        private static readonly Stopwatch stopwatch = new Stopwatch();
        private static readonly string resultFilePath = @"D:result.txt";

        private static void Main()
        {
            try
            {
                LogFile("TimestStringConnectionWinRatetStringBuilderWinRate");
                const int accuracyCorrection = 1000;

                for (var innerTimes = 1; innerTimes <= 40; innerTimes++)
                {
                    var result = CalculateOnce(innerTimes, accuracyCorrection);
                    Console.WriteLine("Calculate Times: {0} {1}", innerTimes, result ? string.Empty : "@@@@@@@@@@@@");
                }

                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
      
        // If stringConnection win return true
        private static bool CalculateOnce(int times, int accuracyCorrection)
        {
            var stringConnectionWinCounter = 0;
            var stringBuilderWinCounter = 0;
            for (var i = 0; i < accuracyCorrection; i++)
            {
                //Console.WriteLine("Stacking fold: " + times);

                stopwatch.Reset();
                var stringConnectionUsedTime = StringConnectionTest(times);
                //Console.WriteLine("String Connection: " + stringConnectionUsedTime);

                stopwatch.Reset();
                var stringBuilderUsedTime = StringBuilderTest(times);
                //Console.WriteLine("String Builder: " + stringBuilderUsedTime);

                if (stringConnectionUsedTime < stringBuilderUsedTime)
                {
                    stringConnectionWinCounter++;
                }

                if (stringConnectionUsedTime > stringBuilderUsedTime)
                {
                    stringBuilderWinCounter++;
                }
            }

            Console.WriteLine("---------------------");
            var stringConnectionWinRate = stringConnectionWinCounter*100.0/
                                          (stringConnectionWinCounter + stringBuilderWinCounter);
            Console.WriteLine("String Connection Win Counter: {0}, rate: {1:0.0000}%.", stringConnectionWinCounter,
                stringConnectionWinRate);
            var stringBuilderWinRate = stringBuilderWinCounter*100.0/
                                       (stringConnectionWinCounter + stringBuilderWinCounter);
            Console.WriteLine("String Builder Win Counter: {0}, rate: {1:0.0000}%.", stringBuilderWinCounter,
                stringBuilderWinRate);

            Console.WriteLine("==# {0} win. #==",
                stringConnectionWinRate > stringBuilderWinCounter ? "String Connection" : "String Builder");

            LogFile(string.Format("{0}t{1}%t{2}%", times, stringConnectionWinRate, stringBuilderWinRate));

            return stringConnectionWinCounter > stringBuilderWinCounter;
        }

        private static long StringConnectionTest(int times)
        {
            var str = string.Empty;

            stopwatch.Start();

            for (var i = 0; i < times; i++)
            {
                str += "Hello World!";
            }

            stopwatch.Stop();

            return stopwatch.ElapsedTicks;
        }

        private static long StringBuilderTest(int times)
        {
            // internal const int DefaultCapacity = 16;
            var str = new StringBuilder();
            //var str = new StringBuilder(times);

            stopwatch.Start();

            for (var i = 0; i < times; i++)
            {
                str.Append("Hello World!");
            }

            stopwatch.Stop();

            return stopwatch.ElapsedTicks;
        }

        private static void LogFile(string content)
        {
            using (var fileStream = File.Open(resultFilePath, FileMode.Append))
            {
                var writer = new StreamWriter(fileStream);
                writer.WriteLine(content);
                writer.Flush();
            }
        }
    }
}
Hendry

About Hendry

不经历复杂的简单,只是一种苍白。

发表评论

电子邮件地址不会被公开。