字符串加入和复杂性?

当我需要连接两个字符串时,我使用String.Format(或StringBuilder,如果它发生在代码中的几个地方)。

我看到一些优秀的程序员不会注意字符串加入复杂性而只是使用’+’运算符。

我知道使用’+’运算符会使应用程序使用更多内存,但复杂性又如何呢?

这是关于我们自己的Jeff Atwood 编码恐怖片的不同字符串连接方法的优秀文章:

替代文字http://sofzh.miximages.com/c%23/

微优化剧场的悲剧悲剧

这是post的要点。

[显示了几个字符串连接方法 ]

把你发痒的小触发手指从编译键上移开,并考虑一下这一点。 哪种方法会更快?

有答案吗? 大!

并且..鼓声请…正确答案:

它。 只是。 不。 物!

这个答案假设您正在谈论运行时复杂性。

使用+创建一个新的字符串对象,这意味着必须将旧字符串对象的内容复制到新字符串对象中。 通过大量连接,例如在紧密循环中,这可以变成O(n ^ 2)操作。

作为一个非正式的证据,说你有以下代码:

 string foo = "a"; for(int i = 0; i < 1000; i++) { foo += "a"; } 

循环的第一次迭代,首先将foo (“a”)的内容复制到一个新的字符串对象中,然后是文字“a”的内容。 那是两份。 第二次迭代有三个副本; 两个来自新foo ,一个来自字面意思“a”。 第1000次迭代将有1001次复制操作。 总份数为2 + 3 + ... + 1001 。 通常,如果在一个循环中,每次迭代只连接一个字符(并且从一个字符长开始),如果迭代次数为n,则将有2 + 3 + ... + n + 1副本。 这与1 + 2 + 3 + ... + n = n(n+1)/2 = (n^2 + n)/2 ,即O(n ^ 2)。

取决于情况。 +有时可以降低代码的复杂性。 请考虑以下代码:

 output = "

" + intro + "

";

这是一条好的,明确的路线。 不需要String.Format。

如果你只使用+一次,你没有任何劣势,它增加了可读性(正如科林皮卡德已经说过的那样)。

据我所知+表示:取左操作数和右操作数并将它们复制到新缓冲区(因为字符串是不可变的)。

因此使用+两次(如在Colin Pickards示例中,您已经创建了2个临时字符串。首先将"

"

添加到intro,然后将"

"到新创建的字符串中。

你必须考虑自己何时使用哪种方法。 即使对于上面看到的一个小例子,如果intro是一个足够大的字符串,性能下降也会很严重。

除非你的应用程序是字符串密集型的(配置文件,配置文件,配置文件!),否则这并不重要。 优秀的程序员将可读性置于高于平凡运营的性能之上。

我认为在复杂性方面,您需要交换重新创建的新字符串来解析格式字符串。 对于例子, "A" + "B" + "C" + "D"表示您必须复制“A”,“AB”和最后“ABC”以形成“ABCD”。 复制是重复的,对吧? 因此,例如,如果您有一个1000字符的字符串,您将与一千个字符串相加,您将复制(1000 + N)个字符串1000次 。 在最坏的情况下,它会导致O(n ^ 2)的复杂性。

Strin.Fomat ,即使考虑解析, StringBuffer应该在O(n)附近。

因为字符串在Java和C#等语言中是不可变的,所以每次连接两个字符串时,必须创建一个新字符串,其中复制了两个旧字符串的内容。

假设平均c字符长的字符串。

现在第一个连接只需复制2 * c个字符,但最后一个必须复制第一个n-1个字符串的串联,即(n-1)* c个字符长,最后一个字符本身,即c个字符长,总共n * c个字符。 对于n个连接,这使得n ^ 2 * c / 2个字符副本,这意味着算法复杂度为O(n ^ 2)。

在实践中的大多数情况下,这种二次复杂度并不明显(正如Jeff Atwood在Robert C. Cartaino链接的博客条目中所示)并且我建议尽可能地将代码编写为可读。

然而,有些情况确实很重要,在这种情况下使用O(n ^ 2)可能是致命的。

在实践中,我已经看到这例如在内存中生成大的Word XML文件,包括base64编码的图片。 由于使用O(n ^ 2)字符串连接,这一代过去需要超过10分钟。 在使用+与StringBuilder替换串联后,同一文档的运行时间减少到10秒以下。

同样地,我看到一个软件使用+进行连接,生成一个非常大的SQL代码作为字符串。 我甚至没有等到完成(已经等了一个多小时),但只是用StringBuilder重写了它。 这个更快的版本在一分钟内完成。

简而言之,只做最可读/最容易编写的内容,只有在你创建一个巨大的字符串时才考虑这个:-)

如果要在几个步骤中构建大字符串,则应使用StringBuilder。 如果您知道它最终会有多大,那么这也是一件好事,然后您可以使用您需要的大小对其进行初始化,并防止重新分配成本。 对于小型操作,使用+运算符不会造成相当大的性能损失,并且会产生更清晰的代码(写入速度更快……)

已经有很多输入,但我一直觉得解决性能问题的最佳方法是了解所有可行解决方案的性能差异,并且对于那些满足性能要求的解决方案,选择最可靠和最可靠的解决方案。支持的。

有许多人使用Big O表示法来理解复杂性,但我发现在大多数情况下(包括理解哪种字符串连接方法效果最好),简单的时间试验就足够了。 只需将strA + strB与strA.Append(strB)在100,000次迭代循环中进行比较,即可了解哪种工作更快。

编译器将字符串文字串联优化为一个字符串文字。 例如:

 string s = "a" + "b" + "c"; 

在编译时优化到以下内容:

 string s = "abc"; 

有关详细信息,请参阅此问题和此MSDN文章 。

编译器将优化:“a”+“b”+“c”将被String.Concat方法替换(不是String.Format一个修复我的评论)

我之前对此进行了基准测试,并且自.NET 1.0或1.1以来它并没有真正产生差异。

那时候如果你有一些进程可以达到一系列代码,这些代码能够将字符串连接几百万次,那么使用String.Concat,String.Format或StringBuilder可以大大提高速度。

现在根本没关系。 至少它已经无关紧要了.Net 2.0无论如何都出现了。 把它放在你的脑海和代码之外,以最简单的方式让你阅读。