【问题标题】:Strings joining and complexity?字符串连接和复杂性?
【发布时间】:2009-08-25 12:57:07
【问题描述】:

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

我看到一些优秀的程序员不会关注字符串连接的复杂性,而只是使用“+”运算符。

我知道使用“+”运算符会使应用程序使用更多内存,但是复杂性呢?

【问题讨论】:

  • @voyager:format 方法在 Java 中有一个小写的“f”,尽管我同意这并不能确定。罗伊应该确认一下。
  • 会不会是 VB.NET 或其他 .NET 语言?我不认为语言有那么重要,我认为原则适用于任何语言。
  • 我在 .Net 环境中使用 c# :-)

标签: c# string complexity-theory


【解决方案1】:

这是我们自己的 Jeff AtwoodCoding Horror 上关于不同字符串连接方法的精彩文章:


(来源:codinghorror.com

The Sad Tragedy of Micro-Optimization Theater

这是帖子的要点。

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

拿起你发痒的小扳机指 关闭编译键并考虑 这是一分钟。其中哪一个 方法会更快吗?

有答案吗?太好了!

还有..请打鼓..正确的 回答:

它。只是。没有。很重要!

【讨论】:

  • 这是一本好书。这让我不再关心琴弦是如何完成的。记住不要优化,直到你也有。
  • 问题是关于复杂性的。 Jeff 的文章是关于性能的。
【解决方案2】:

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

使用+ 创建一个新的字符串对象,这意味着必须将两个旧字符串对象的内容复制到新对象中。对于大量的连接,例如在一个紧密的循环中,这可以变成一个 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)。

【讨论】:

    【解决方案3】:

    视情况而定。 + 有时可以降低代码的复杂性。考虑以下代码:

    output = "<p>" + intro + "</p>";
    

    这是一条很好的、清晰的线。不需要 String.Format。

    【讨论】:

    • 我还是宁愿把它作为 output = string.Format("

      {0}

      ", intro) 以防输出必须改变。我并不是说这不是一条好的、清晰的路线,但 string.Format 可以为您提供更多的保护,使其免受未来变化的影响。
    【解决方案4】:

    如果你只使用一次 +,你没有任何缺点,而且它增加了可读性(正如 Colin Pickard 已经说过的)。

    据我所知 + 的意思是:将左操作数和右操作数复制到一个新的缓冲区中(因为字符串是不可变的)。

    所以使用 + 两次(如在 Colin Pickards 示例中,您已经创建了 2 个临时字符串。首先将 "&lt;p&gt;" 添加到 intro 时,然后将 "&lt;/p&gt;" 添加到新创建的字符串时。

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

    【讨论】:

      【解决方案5】:

      除非您的应用程序非常密集(配置文件、配置文件、配置文件!),否则这并不重要。优秀的程序员将可读性置于日常操作的性能之上。

      【讨论】:

        【解决方案6】:

        我认为就复杂性而言,您需要重复新创建的字符串来解析格式字符串。 例如"A" + "B" + "C" + "D" 意味着您必须复制“A”、“AB”,最后复制“ABC”才能形成“ABCD”。复制就是重复,对吧?因此,例如,如果您有一个 1000 个字符串,您将与千个字符串相加,您将复制 (1000+N) 个字符串 1000 次。在最坏的情况下会导致 O(n^2) 复杂度。

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

        【讨论】:

          【解决方案7】:

          因为字符串在 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 重写了它。这个更快的版本在一分钟内完成。

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              【解决方案9】:

              已经有很多意见,但我一直认为解决性能问题的最佳方法是了解所有可行解决方案的性能差异,对于那些满足性能要求的解决方案,选择最可靠的解决方案也是最受支持的。

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

              【讨论】:

                【解决方案10】:

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

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

                在编译时优化如下:

                string s = "abc";
                

                请参阅this questionthis MSDN article 了解更多信息。

                【讨论】:

                  【解决方案11】:

                  编译器会优化:将“a”+“b”+“c”替换为String.Concat方法(不是String.Format一个作为固定我的cmets)

                  【讨论】:

                  • @kamarey 如果您同意 dtb 的评论,为什么不编辑或删除答案?
                  【解决方案12】:

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

                  那时,如果您有某个进程要执行几百万次连接字符串的代码行,那么您可以通过使用 String.Concat、String.Format 或 StringBuilder 获得巨大的速度提升。

                  现在根本不重要了。至少自从 .Net 2.0 出现以来,这并不重要。把它从你的脑海中抛开,并以任何让你最容易阅读的方式编写代码。

                  【讨论】:

                    猜你喜欢
                    • 2015-09-18
                    • 2013-03-02
                    • 2019-01-16
                    • 2018-01-06
                    • 2021-11-27
                    • 2017-07-30
                    • 2013-11-30
                    • 2019-10-24
                    相关资源
                    最近更新 更多