代码之家  ›  专栏  ›  技术社区  ›  MetallicPriest

赋值时字符串串联是否有效?

  •  2
  • MetallicPriest  · 技术社区  · 7 年前

    我知道使用“+”串联运算符构建字符串效率非常低,这就是为什么建议使用StringBuilder类的原因,但我想知道这种模式是否也很低?

    String some = a + "\t" + b + "\t" + c + "\t" + d + "\t" + e;
    

    我想在这里编译器会优化分配,好吗?

    3 回复  |  直到 7 年前
        1
  •  6
  •   Karol Dowbecki    7 年前

    编译器将内联此特定示例:

    String a = "a";
    String b = "bb";
    String c = "ccc";
    String some = a + "\t" + b + "\t" + c;
    

    Java 9将使用 invokedynamic with makeConcatWithConstants 提高效率。按照 javap -v 输出:

    Code:
      stack=3, locals=5, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String bb
         5: astore_2
         6: ldc           #4                  // String ccc
         8: astore_3
         9: aload_1
        10: aload_2
        11: aload_3
        12: invokedynamic #5,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        17: astore        4
        19: return
    

    但是如果 a b c 编译器将进一步优化代码:

    final String a = "a";
    final String b = "bb";
    final String c = "ccc";
    String some = a + "\t" + b + "\t" + c;
    

    some 将加载一个常量值:

    Code:
      stack=1, locals=5, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String bb
         5: astore_2
         6: ldc           #4                  // String ccc
         8: astore_3
         9: ldc           #5                  // String a\tbb\tccc
        11: astore        4
        13: return
    

    在其他情况下,例如 for 循环编译器可能无法生成优化的代码,因此 StringBuilder 可能会更快。

        2
  •  3
  •   Holger    7 年前

    您的前提是使用“+”串联运算符构建字符串非常低效,这是不正确的。首先,字符串连接本身不是一个便宜的操作,因为它意味着创建一个包含所有连接字符串的新字符串,因此需要复制字符内容。但这总是适用的,不管 怎样 你做到了。

    当你使用 + 接线员,你说你想做什么,却不说怎么做。甚至Java语言规范也不需要特定的实现策略,除非编译时常数的级联必须在编译时完成。所以对于编译时常量, + 操作人员 最有效的解决方案。

    实际上,从java5到java8的所有常用编译器都使用 StringBuilder 在引擎盖下面(在Java5之前,他们使用 StringBuffer )这适用于像您这样的语句,因此用手册替换它 字符串拼接 使用不会得到太多好处。通过提供一个合理的初始容量,您可能比典型的编译器生成的代码要好一点,但仅此而已。

    从Java9开始,编译器生成一个 invokedynamic 允许运行时提供执行连接的实际代码的指令。这可能是 字符串拼接 代码类似于过去使用的代码,但也完全不同。最值得注意的是,运行时提供的代码可以访问特定于实现的特性,而应用程序代码不能访问这些特性。现在,字符串连接通过 + 甚至可以比 字符串拼接 基于代码。

    因为这只适用于一个串联表达式,当使用多个语句甚至循环执行字符串构造时,使用 字符串拼接 在整个构建过程中,一致性可能比多个串联操作更快。但是,由于代码在优化环境中运行,JVM识别了其中的一些模式,因此甚至不能肯定这一点。

    这是时候记住旧规则,只有在性能出现实际问题时才尝试优化性能。并始终使用公正的测量工具验证尝试的优化是否真的提高了性能。有很多关于性能优化技巧的误解,无论是错误的还是过时的。

    除非您有重复的部分,并且希望减小类文件的大小

        3
  •  2
  •   Oleg Cherednik    7 年前

    在一般情况下,字符串与 + 并使用 StringBuilder 绝对正确有效。但是在不同的情况下 + 比使用效率低 字符串拼接 .

    字符串串联不在循环中-高效!!!!

    这使得性能很好,因为JVM使用 字符串拼接 .

    String some = a + "\t" + b + "\t" + c + "\t" + d + "\t" + e;
    

    这是正常的,因为JVM在内部将此代码更改为以下代码:

    String some = new StringBuilder().append(a).append('\t').append(c).append('\t')
                                     .append(d).append('\t').append(e).toString();
    

    附笔。 字符串拼接 具有内部缓冲区 char[] . 如果您知道结果字符串的长度,那么最好在开始时保留整个缓冲区。例如,如果最后一根弦 最多1024个字符 ,那么你可以 new StringBuilder(1024)

    循环中的字符串串联-效率不高!!!!

    这会使性能变差,因为JVM不能用一个 字符串拼接 ,像这样:

    StringBuilder buf = new StringBuilder();
    
    for (int i = 0; i < 10; i++)
        buf.append(a).append('\t').append(c).append('\t')
           .append(d).append('\t').append(e).append('t');
    
    String some = buf.toString();
    

    但是,JVM仍然能够优化每个循环迭代中的所有连接;如下所示:

    String some = "";
    
    for (int i = 0; i < 10; i++) {
        some = new StringBuilder(some).append(a).append('\t').append(c).append('\t')
                                   .append(d).append('\t').append(e).append('t');
    }
    

    如您所见,在循环中使用字符串串联有一些缺点。