当前位置:  开发笔记 > 编程语言 > 正文

C#中的字符串操作优化

如何解决《C#中的字符串操作优化》经验,为你挑选了3个好方法。

以下C#代码需要5分钟才能运行:

int i = 1;
string fraction = "";
while (fraction.Length < 1000000)
{
    fraction += i.ToString();
    i++;
}

像这样"优化它"使它在1.5秒内运行:

int i = 1;
string fraction = "";
while (fraction.Length < 1000000)
{
    // concatenating strings is much faster for small strings
    string tmp = "";
    for (int j = 0; j < 1000; j++)
    {
        tmp += i.ToString();
        i++;
    }
    fraction += tmp;
}

编辑:有人建议使用StringBuilder,这也是一个很好的建议,这出现在0.06s:

int i = 1;
StringBuilder fraction = new StringBuilder();
while (fraction.Length < 1000000)
{
    fraction.Append(i);
    i++;
}

四处寻找最佳值j是另一个时间的主题,但为什么这个非显而易见的优化工作如此之好呢?另外,在一个相关的主题上,我听说它应该永远不要使用+带字符串的运算符,赞成string.Format(),这是真的吗?



1> Jon Skeet..:

我根本没有得到你的结果.在我的盒子上,StringBuilder赢得了胜利.你能发布完整的测试程序吗?这是我的,有三个变体 - 你的字符串连接优化,"简单"的StringBuilder,以及具有初始容量的StringBuilder.我已经增加了限制因为我的盒子上的速度太快而无法进行有效测量.

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

public class Test
{
    const int Limit = 4000000;

    static void Main()
    {
        Time(Concatenation, "Concat");
        Time(SimpleStringBuilder, "StringBuilder as in post");
        Time(SimpleStringBuilderNoToString, "StringBuilder calling Append(i)");
        Time(CapacityStringBuilder, "StringBuilder with appropriate capacity");
    }

    static void Time(Action action, string name)
    {
        Stopwatch sw = Stopwatch.StartNew();
        action();
        sw.Stop();
        Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    static void Concatenation()
    {
        int i = 1;
        string fraction = "";
        while (fraction.Length < Limit)
        {
            // concatenating strings is much faster for small strings
            string tmp = "";
            for (int j = 0; j < 1000; j++)
            {
                tmp += i.ToString();
                i++;
            }
            fraction += tmp;            
        }
    }

    static void SimpleStringBuilder()
    {
        int i = 1;
        StringBuilder fraction = new StringBuilder();
        while (fraction.Length < Limit)
        {
            fraction.Append(i.ToString());
            i++;
        }
    }

    static void SimpleStringBuilderNoToString()
    {
        int i = 1;
        StringBuilder fraction = new StringBuilder();
        while (fraction.Length < Limit)
        {
            fraction.Append(i);
            i++;
        }
    }

    static void CapacityStringBuilder()
    {
        int i = 1;
        StringBuilder fraction = new StringBuilder(Limit + 10);
        while (fraction.Length < Limit)
        {
            fraction.Append(i);
            i++;
        }
    }
}

结果如下:

Concat: 5879ms
StringBuilder as in post: 206ms
StringBuilder calling Append(i): 196ms
StringBuilder with appropriate capacity: 184ms

你的串联比第一个解决方案更快的原因很简单 - 你正在做几个"廉价"的连接(每次复制相对较少的数据)和相对较少的"大"连接(到目前为止整个字符串) .在原文中,每一步都会复制到目前为止获得的所有数据,这显然更加昂贵.



2> Mitch Wheat..:

使用StringBuilder连接超过(大约)5个字符串(结果可能略有不同).另外,为StringBuilder的构造函数提供预期最大大小的提示.

[更新]:只评论您对该问题的修改.StringBuilder如果你对连接字符串的最终大小有一个近似(或确切)的想法,你也可以提高性能,因为这会减少它必须执行的内存分配数量:

// e.g. Initialise to 10MB
StringBuilder fraction = new StringBuilder(10000000);



3> jishi..:

您可能会看到前1000个字符几乎没有时间反对最后1000个字符.

我认为耗时的部分是每次添加一个对你的计算机来说很难的字符时,将大字符串实际复制到一个新的内存区域.

您可以轻松地将优化与通常使用流进行比较,使用缓冲区.较大的块通常会导致更好的性能,直到您达到不再有任何差异的临界大小,并且在处理少量数据时开始变得不利.

但是,如果您从一开始就定义了一个具有适当大小的char数组,那么它可能会非常快速,因为它不会一遍又一遍地复制它.

推荐阅读
雯颜哥_135
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有