连接字符串最有效的方法?

时间:2008-08-21 20:27:15

标签: c# .net string optimization

连接字符串的最有效方法是什么?

17 个答案:

答案 0 :(得分:258)

.NET性能大师

Rico Mariani在这个主题上有an article。它并不像人们怀疑的那么简单。基本建议如下:

  

如果您的模式如下:

     

x = f1(...) + f2(...) + f3(...) + f4(...)

     

这是一个concat而且它很活泼,StringBuilder可能无济于事。

     

如果您的模式如下:

     

if (...) x += f1(...)
  if (...) x += f2(...)
  if (...) x += f3(...)
  if (...) x += f4(...)

     

然后你可能想要StringBuilder。

Yet another article to support this claim来自Eric Lippert,他详细描述了对一行+级联进行的优化。

答案 1 :(得分:139)

StringBuilder.Append()方法比使用+运算符要好得多。但我发现,当执行1000个或更少的连接时,String.Join()甚至比StringBuilder更有效。

StringBuilder sb = new StringBuilder();
sb.Append(someString);

String.Join的唯一问题是你必须使用公共分隔符连接字符串。 (编辑:)正如@ryanversaw指出的那样,你可以制作分隔符string.Empty。

string key = String.Join("_", new String[] 
{ "Customers_Contacts", customerID, database, SessionID });

答案 2 :(得分:70)

有6种类型的字符串连接:

  1. 使用加号(+)符号。
  2. 使用string.Concat()
  3. 使用string.Join()
  4. 使用string.Format()
  5. 使用string.Append()
  6. 使用StringBuilder
  7. 在一项实验中,已经证明string.Concat()是最好的方法,如果单词小于1000(大约),如果单词大于1000,则应使用StringBuilder

    有关详细信息,请查看此site

      

    string.Join()vs string.Concat()

         

    这里的string.Concat方法相当于带有空分隔符的string.Join方法调用。附加一个空字符串很快,但不这样做更快,所以 string.Concat 方法在这里会更好。

答案 3 :(得分:51)

来自Chinh Do - StringBuilder is not always faster

经验法则

  • 连接三个或更少的动态字符串值时,请使用传统的字符串连接。

  • 连接三个以上的动态字符串值时,请使用StringBuilder。

  • 从多个字符串文字构建一个大字符串时,请使用@ string literal或inline +运算符。

大多数的时间StringBuilder是你最好的选择,但有一些案例如该帖子所示,你至少应该考虑每种情况。

答案 4 :(得分:11)

如果你在一个循环中运行,StringBuilder可能就是这样;它可以节省您定期创建新字符串的开销。但是,在只运行一次的代码中,String.Concat可能没问题。

然而,Rico Mariani(.NET优化大师)made up a quiz他在最后声明,在大多数情况下,他推荐String.Format。

答案 5 :(得分:7)

这是我用于大型NLP应用程序十多年来发展最快的方法。我有IEnumerable<T>和其他输入类型的变体,有和没有不同类型的分隔符(CharString),但这里我展示了 连接的简单情况数组中的所有字符串 成一个字符串,没有分隔符。这里的最新版本是在 C#7 .NET 4.7 上开发和单元测试的。

提高性能有两个关键;首先是预先计算所需的确切总大小。当输入是如此处所示的数组时,此步骤是微不足道的。对于处理IEnumerable<T>而言,值得首先将字符串收集到一个临时数组中以计算总数(因为从技术上讲,该数组需要避免每个元素多次调用ToString(),因为有可能 - 这样做会改变字符串连接操作的预期语义。

接下来,考虑到最终字符串的总分配大小, 就地构建结果字符串 可以获得性能的最大提升。这样做需要(可能是有争议的)暂时中止最初分配为零的新String的不变性的技术。除此之外,还有任何此类争议......

  

...请注意,这是此页面上唯一的批量连接解决方​​案,它完全避免了String构造函数的额外的分配和复制

完整代码:

/// <summary>
/// Concatenate the strings in 'rg', none of which may be null, into a single String.
/// </summary>
public static unsafe String StringJoin(this String[] rg)
{
    int i;
    if (rg == null || (i = rg.Length) == 0)
        return String.Empty;

    if (i == 1)
        return rg[0];

    String s, t;
    int cch = 0;
    do
        cch += rg[--i].Length;
    while (i > 0);
    if (cch == 0)
        return String.Empty;

    i = rg.Length;
    fixed (Char* _p = (s = new String(default(Char), cch)))
    {
        Char* pDst = _p + cch;
        do
            if ((t = rg[--i]).Length > 0)
                fixed (Char* pSrc = t)
                    memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1));
        while (pDst > _p);
    }
    return s;
}

[DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb);

我应该提一下,这段代码与我自己使用的内容略有不同。在原文中,我来自 C# call the cpblk IL instruction进行实际复制。为了简化和在这里的代码中的可移植性,我用P / Invoke memcpy取而代之,正如你所看到的那样。要获得x64(but maybe not x86)的最高性能,您可能需要使用 cpblk 方法。

答案 6 :(得分:6)

从此MSDN article

  

有一些与之相关的开销   两者都创建一个StringBuilder对象   在时间和记忆中。在一台机器上   快速内存,一个StringBuilder成为   如果你做五个左右,那是值得的   操作。根据经验,我   会说10个或更多字符串操作   是开销的理由   任何机器,即使是较慢的机器。

因此,如果您信任MSDN,请使用StringBuilder,如果您必须执行超过10个字符串操作/连接 - 否则使用“+”的简单字符串连接就可以了。

答案 7 :(得分:5)

添加其他答案,请记住StringBuilder can be told an initial amount of memory to allocate

  

capacity 参数定义可以存储在当前实例分配的内存中的最大字符数。其值分配给Capacity属性。如果要存储在当前实例中的字符数超过此 capacity 值,则StringBuilder对象会分配额外的内存来存储它们。

     

如果 capacity 为零,则使用特定于实现的默认容量。

重复附加到尚未预先分配的StringBuilder可能会导致大量不必要的分配,就像重复连接常规字符串一样。

如果您知道最终字符串的长度,可以轻松计算它,或者可以对常见情况作出有根据的猜测(分配太多不一定是坏事),您应该将此信息提供给构造函数或Capacity属性。 特别是在运行性能测试时将StringBuilder与其他方法(如String.Concat)进行比较,这些方法在内部执行相同的操作。您在网上看到的任何测试都不包括StringBuilder在其比较中的预分配是错误的。

如果您无法对大小做出任何猜测,那么您可能正在编写一个实用函数,它应该有自己的可选参数来控制预分配。

答案 8 :(得分:4)

如果要连接string literals,请指出应使用+运算符也很重要。

  

使用+运算符连接字符串文字或字符串常量时,编译器会创建一个字符串。没有运行时连接。

How to: Concatenate Multiple Strings (C# Programming Guide)

答案 9 :(得分:3)

以下可能是连接多个字符串的另一种替代解决方案。

String str1 = "sometext";
string str2 = "some other text";

string afterConcate = $"{str1}{str2}";

string interpolation

答案 10 :(得分:2)

最有效的方法是使用StringBuilder,如下所示:

StringBuilder sb = new StringBuilder();
sb.Append("string1");
sb.Append("string2");
...etc...
String strResult = sb.ToString();

@jonezy:如果你有几个小东西,String.Concat就可以了。但是,如果你连接数兆字节的数据,那么你的程序可能就会出现问题。

答案 11 :(得分:2)

试试这两段代码,你就会找到解决方案。

 static void Main(string[] args)
    {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 10000000; i++)
        {
            s.Append( i.ToString());
        }
        Console.Write("End");
        Console.Read();
    }

Vs的

static void Main(string[] args)
    {
        string s = "";
        for (int i = 0; i < 10000000; i++)
        {
            s += i.ToString();
        }
        Console.Write("End");
        Console.Read();
    }

你会发现第一个代码会很快结束,内存也会很好。

第二个代码可能是内存可以正常,但需要更长时间......更长时间。 因此,如果您有许多用户的应用程序并且您需要速度,请使用1st。如果你有一个短期一个用户应用程序的应用程序,也许你可以使用两个或第二个将更多&#34;自然&#34;对于开发人员。

干杯。

答案 12 :(得分:1)

另一种解决方案:

在循环内部,使用List而不是string。

List<string> lst= new List<string>();

for(int i=0; i<100000; i++){
    ...........
    lst.Add(...);
}
return String.Join("", lst.ToArray());;

非常快。

答案 13 :(得分:1)

System.String是不可变的。当我们修改字符串变量的值时,会将新内存分配给新值并释放先前的内存分配。 System.StringBuilder被设计为具有可变字符串的概念,其中可以执行各种操作而无需为修改的字符串分配单独的内存位置。

答案 14 :(得分:1)

这实际上取决于您的使用模式。 string.Join,string,Concat和string.Format之间的详细基准可以在这里找到:String.Format Isn't Suitable for Intensive Logging

(这实际上是我给this问题的答案)

答案 15 :(得分:0)

这取决于代码。 StringBuilder通常效率更高,但是如果你只连接几个字符串并在一行中完成所有操作,代码优化可能会为你处理它。考虑代码的外观也很重要:对于较大的集合,StringBuilder将使其更易于阅读,对于较小的集合,StringBuilder只会添加不必要的混乱。

答案 16 :(得分:0)

对于两个字符串,您绝对不想使用StringBuilder。有一些阈值,高于该阈值,StringBuilder开销小于分配多个字符串的开销。

因此,对于更多2-3个字符串,请使用DannySmurf's code。否则,只需使用+运算符。