28

我知道 Javac 编译器能够+使用StringBuilder/转换字符串连接StringBuffer,我很想知道从哪个版本开始引入了这个更改?

我正在使用这个示例代码:

public class Main {
  public static void main(String[] args) {
      String a = args[0];
      String s = "a";
      s = s + a;
      s = s + "b";
      s = s + "c";
      s = s + "d";
      s = s + "e";
      System.out.println(s);
  }
}

到目前为止,我已经尝试过javac 1.8.0_121,和.javac 1.6.0_20javac 1.5.0_22java 1.4.2_19

这是我看到使用的字节码javap -c示例1.4.2_19

6:  astore_2
7:  new #3; //class StringBuffer
10: dup
11: invokespecial   #4; //Method java/lang/StringBuffer."<init>":()V
14: aload_2
15: invokevirtual   #5; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
18: aload_1
19: invokevirtual   #5; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
22: invokevirtual   #6; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;

所有 4 个版本似乎都在使用 StringBuilder/StringBuffer 优化,所以我很想知道这个更改是从哪个 Javac 版本开始引入的?

4

4 回答 4

32

这是来自版本 1 的语言规范的引用:

实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃中间String对象。为了提高重复字符串连接的性能,Java 编译器可以使用StringBuffer类(第 20.13 节)或类似技术来减少String通过评估表达式创建的中间对象的数量。

回到当时,他们有StringBuffer而不是StringBuilder.

也是来自StringBufferJDK1.0.2的引用:

这个类是一个可增长的字符缓冲区。它主要用于创建字符串。编译器使用它来实现“+”运算符。

于 2017-02-27T14:21:39.367 回答
11

我查阅了 Java 语言规范,第一版(从 1996 年开始)。不容易找到,但在这里。即使在那时,关于连接优化的文章也在那里:

实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃中间 String 对象。为了提高重复字符串连接的性能,Java 编译器可以使用 StringBuffer 类(第 20.13 节)或类似技术来减少通过评估表达式创建的中间字符串对象的数量。

该规范与StringBuffer当时有关,但StringBuilder(当前 JLS 措辞所指)可能被认为性能更好,因为它的方法不同步。

然而,这并不意味着人们应该始终依赖优化。例如,循环中的字符串连接不会得到优化。

于 2017-02-27T14:22:31.140 回答
5

JLS 已经给出了一些答案。我只想指出 StringBuffer (https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html)自 1.0 以来就在那里,而

StringBuilder( https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html ) 出现在 1.5 版中。请参阅since:相应的 javadocs 部分。

于 2017-02-27T14:25:44.200 回答
5

这没有回答问题,但我只想补充一点,在 jdk-9 中这StringBuilder::append允许的策略之一,但不是默认策略。

private enum Strategy {
   /**
    * Bytecode generator, calling into {@link java.lang.StringBuilder}.
    */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
     BC_SB_SIZED_EXACT,

   /**
    * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
    * This strategy also tries to estimate the required storage.
    */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
     MH_INLINE_SIZED_EXACT
}

它实际上是invokedynamic字符串连接的字节码,所以它的实现现在是特定于 JRE 的,而不是编译器。顺便说一句,默认策略是:MH_INLINE_SIZED_EXACT

于 2017-02-28T19:09:50.283 回答