2

我刚刚进入函数式编程并试图了解何时应该使类/属性可变。

当处理大量字符串连接时,我们知道最好使用 StringBuilder,例如:

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

namespace ConsoleApplication3
{
    internal class Program
    {
        private static string myStr;
        private static readonly StringBuilder mySb = new StringBuilder();

        private static void Main(string[] args)
        {
            Profile("+", 100000, () => myStr = myStr + "a"); // Takes 2236 ms
            Profile("SB", 100000, () => mySb.Append("a")); // Takes 1 ms
        }

        private static void Profile(string description, int iterations, Action func)
        {
            // clean up
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            // warm up 
            func();

            Stopwatch watch = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                func();
            }
            watch.Stop();
            Console.Write(description);
            Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
        }
    }
}

这是众所周知的情况,在这种情况下,通过与运算符连接字符串的性能要高得多。我的假设是通过创建更少的字符串来实现更好的性能。StringBuilder+StringBuilder

性能和不变性之间是否存在平衡,或者这种情况是出于某种原因的例外?

4

1 回答 1

6

有效地连接字符串不是关于可变结构与不可变结构,而是更多关于选择正确的数据结构和评估策略以支持O(1)追加。

通常,各种树用于支持快速追加,从而最大限度地提高共享,并最大限度地减少复制。示例结构包括绳索手指树

在某些情况下,惰性求值也有帮助(例如,如果连接涉及复制,则可以延迟到是否以及何时实际需要字符串的尾部)。在这种情况下,严格的数据结构可能会产生额外的复制开销,做的工作超出了必要的范围。

在您的情况下,我怀疑+涉及参数的严格副本(即O(n+m))工作,而字符串构建器可以通过摊销字符串缓冲区的重新分配来避免一些工作(为您提供树状性能,以要求线性使用结构和失去线程安全为代价)。

于 2012-07-03T15:46:55.800 回答