连接字符串的最有效方法是什么?
答案 0 :(得分:258)
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种类型的字符串连接:
+
)符号。string.Concat()
。string.Join()
。string.Format()
。string.Append()
。StringBuilder
。在一项实验中,已经证明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>
和其他输入类型的变体,有和没有不同类型的分隔符(Char
,String
),但这里我展示了 连接的简单情况数组中的所有字符串 成一个字符串,没有分隔符。这里的最新版本是在 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,请指出应使用+
运算符也很重要。
使用+运算符连接字符串文字或字符串常量时,编译器会创建一个字符串。没有运行时连接。
答案 9 :(得分:3)
以下可能是连接多个字符串的另一种替代解决方案。
String str1 = "sometext";
string str2 = "some other text";
string afterConcate = $"{str1}{str2}";
答案 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。否则,只需使用+运算符。