【问题标题】:Yet again on string append vs concat vs +再次在字符串追加 vs concat vs +
【发布时间】:2012-02-16 06:17:46
【问题描述】:

可能是我正在分裂头发,但我想知道以下情况:

String newString = a + b + c;  //case 1


String newString = a.concat(b).concat(c);   //case 2

StringBuilder newString = new StringBuilder(); //case 3
newString.append(a);
newString.append(b);    
newString.append(c);

哪个最好用?

我的意思是无论如何

阅读这些,其他帖子说案例 3 并不是最佳性能,其他人认为案例 1 最终会出现在案例 3 中等等。

更具体一点。

例如,抛开一切不谈,如果您必须维护他的代码,哪种风格更适合从其他程序员那里看到?

或者您认为哪种编程效率更高?
或者你会认为更快等等。

我不知道该怎么表达。

一个答案,例如案例 3 可以更快,但绝大多数程序员更喜欢案例 1,因为它最易读,如果它以某种方式精心阐述,也可以接受

【问题讨论】:

  • 我不介意投反对票。我非常介意匿名投反对票
  • 这个问题可能是其他几个人的欺骗。 (由于没有人与骗子有联系,因此被投票赞成。)
  • @Cratylus,希望我能一次又一次地支持你的评论。但谁在乎,没有匿名投票就不是这样。

标签: java string performance coding-style


【解决方案1】:

案例 3 在大多数方面都更好。在第 3 种情况下,您最终不会创建 3 个字符串对象。由于字符串不变性,2 将为每个 +(或)连接创建字符串对象的开销。

编辑:重新阅读文档并同意大多数 cmets,案例 1 是案例 3。

【讨论】:

  • 通常当您在性能很重要的地方进行文本操作时,您会处理 (a) 更大的操作量; (b) 更复杂的操作,例如搜索和插入或删除文本。
  • 现代 JVM 在运行时将 case 1 转换为 case 3,因此不会有 3 个 String 对象。但是如果有很多顺序连接(在不同的语句中)或在一个循环中,那么是的,每个语句都会创建一个新的不可变字符串。
  • 考虑两种情况:'String j = "Java"; j += "岩石";'字符串 j = "Java"; j = j.concat("岩石");在运行时,“Java”和“Rocks”将存储在一个专用的数据结构中,并且这些文字将在源代码中被对该数据结构中相应条目的引用替换。实际上,字符串池是在加载类时而不是在运行包含字符串文字的代码时填充的。这意味着每个 sn-ps 将只创建连接的结果。所以 IMO 没有一个选项会创建 3 个 String 对象。
【解决方案2】:

Case 3 是性能最高的形式,但是 JVM 将 case 1 转换为 case 3。

但我认为案例 2 是最糟糕的,它不像案例 1 那样可读,也不像案例 3 那样执行。

如果您想在循环中连接字符串,只需使用案例 3,您可以轻松测试性能增益,但如果它不在循环中(或者您没有在序列中附加大量字符串),它们几乎是一样。

不应该使用 + 运算符的情况是:

String a = "";
for (String x : theStrings) {
    a += x;
}

String a = b + c;
a = a + d;
a = a + e;
a = a + f;

【讨论】:

    【解决方案3】:

    案例1简洁,表达意图清晰,相当于案例3。

    案例 2 效率较低,可读性也较差。

    案例 3 几乎与案例 1 一样有效,但更长,可读性更差。

    仅当您必须在循环中进行连接时,才更好地使用案例 3。否则,编译器会将案例 1 编译为案例 3(除了它使用 new StringBuilder(a) 构造 StringBuilder),这使得它比案例 3 更高效)。

    【讨论】:

    • +1:案例 2 创建一个临时对象,就像其他案例一样,所以我不清楚它的效率较低,但同意它最不可能清楚。 (部分是因为它最不常用)
    • 它有 1 个临时对象(实际上是 2 个,如果算上 char 数组),因为只有 3 个字符串要连接。如果添加更多字符串,则每个字符串会多出一个临时对象。
    • 同意它也不能扩展。对于a.concat(b),它实际上稍微好一点。
    • 首先,我用Java编程、阅读技术文章、参加Java新闻组等已经14年多了。在这么长的时间里,你学到了很多东西。其次,这个问题很经典。第三,源代码确实可用。第四,使用javap -c Test.class 可以发现编译器如何编译s = a + b + c
    • 编译器不会把case 1编译成case 3,而是JVM在运行时把case 1翻译成case 3。
    【解决方案4】:

    对于简单的情况,使用+ 显然更具可读性。

    对于罕见的更复杂的情况,StringBuilder 是有意义的。如果您要跨行拆分,那么+ 很快就会拖累性能。循环从 O(n) 性能到 O(n^2) - 如果 n 很小,则不是问题,但如果 n 在尴尬的情况下可能很大,则问题很大(可能是在做一些关键的事情时,或者当某人怀有恶意时)。

    与其他答案相反,如果您只有三个字符串,concat 可能是性能赢家。我有精神吗?不。考虑其他两个选项。您正在创建一个带有数组的 StringBuilder 对象。附加字符串很可能需要替换数组,并且您可能在数组末尾留下奇数位(分配有一定的粒度,因此一些额外的chars 可能会或可能不会增加内存使用量)。如果您讨厌每个阅读您的代码的人,您可以计算必要的容量。最好的情况,两个数组(一个用于StringBuilder,一个用于String(过去八年没有共享))每个结果文本的大小和两个其他对象(StringBuilderString)。现在对于concat,您将使用两个数组分配给String 对象,但第一个数组将c.length() 更短。这是一场胜利!好吧,不是为了可读性。

    免责声明:JVM 可能会进行疯狂的优化,包括逃逸分析后的堆栈分配和核心类的特殊外壳。与手动混淆代码相比,它们更有可能优化常见和简单的代码。

    【讨论】:

      【解决方案5】:

      在使用 JDK 1.8 进行性能分析时,我发现案例 2 在性能方面是赢家。

      class Solution {
      public static void main(String[] args) {
         String s ="ABC", s1 = "", s2 = "", s3 = "";
         long t1 = System.nanoTime();
         for(int i = 1; i < 11; i++) {
             s1 = s1 + s;
         }
         System.out.println(System.nanoTime() - t1);
         System.out.println(s1);
      
         long t2 = System.nanoTime();
         for(int i = 1; i < 11; i++) {
             s2 = s2.concat(s);
         }
         System.out.println(System.nanoTime() - t2);
         System.out.println(s2);
      
         long t3 = System.nanoTime();
         StringBuilder sb = new StringBuilder();
         for(int i = 1; i < 11; i++) {
             sb.append(s);
         }
         s3 = sb.toString();
         System.out.println(System.nanoTime() - t3);
         System.out.println(s3);
       }
      }
      

      结果:

      40615

      ABCABCABCABCABCABCABCABCABCABC

      9157

      ABCABCABCABCABCABCABCABCABCABC

      20243

      ABCABCABCABCABCABCABCABCABCABC

      【讨论】:

      • JVM 的字符串实习会影响您的结果
      • @user85421 非常有趣的链接。在 junit 中看到不可预测的结果后,我得出了相同的结论(一旦一个方法获胜并且下一次运行它是另一种更快的方法)。下次我需要测试算法性能时,我会试试这个 JMH。
      【解决方案6】:

      除了上述之外,在 JEP 280 下对字符串连接进行了显着的性能改进。

      请参考 https://openjdk.java.net/jeps/280 和解释 https://dzone.com/articles/jdk-9jep-280-string-concatenations-will-never-be-t

      简而言之,这意味着从 Java 9 开始,"Hello " + "world" 字符串连接是一种首选方式,即使考虑到性能也是如此。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-09
        • 2012-11-04
        • 2014-09-13
        • 2011-06-07
        相关资源
        最近更新 更多