何时编译器会在StringBuilder上选择StringBuffer进行字符串连接

时间:2015-03-24 01:03:16

标签: java java-8

当我注意到有关字符串连接的这一点时,我正在查看String Javadoc

  

Java语言为字符串提供特殊支持   连接运算符(+),以及将其他对象转换为   字符串。 String串联通过实现   StringBuilder(或StringBuffer

从Java 8 JLS 15.8.1开始,它是编译器的选择(强调我的):

  

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

我做了一个小程序,看看它编译成什么

public class Tester {

    public static void main(String[] args) {
        System.out.println("hello");
        for (int i = 1; i < 5; i++) {
            String s = "hi " + i;
            System.out.println(s);
        }
        String t = "me";
        for (int i = 1; i < 5; i++) {
            t += i;
            System.out.println(t);
        }
        System.out.println(t);
    }
}

运行javap -c Tester时的输出显示正在使用StringBuilder

Compiled from "Tester.java"
public class Tester {
  public Tester();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String hello
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: iconst_1
       9: istore_1
      10: iload_1
      11: iconst_5
      12: if_icmpge     48
      15: new           #5                  // class java/lang/StringBuilder
      18: dup
      19: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      22: ldc           #7                  // String hi
      24: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: iload_1
      28: invokevirtual #9                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      31: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: astore_2
      35: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      38: aload_2
      39: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      42: iinc          1, 1
      45: goto          10
      48: ldc           #11                 // String me
      50: astore_1
      51: iconst_1
      52: istore_2
      53: iload_2
      54: iconst_5
      55: if_icmpge     90
      58: new           #5                  // class java/lang/StringBuilder
      61: dup
      62: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      65: aload_1
      66: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      69: iload_2
      70: invokevirtual #9                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      73: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      76: astore_1
      77: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      80: aload_1
      81: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      84: iinc          2, 1
      87: goto          53
      90: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      93: aload_1
      94: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      97: return
}

我已经查看了一些问题,这些问题告诉StringBuilder由于StringBuffer中的同步而导致StringBuilder通常更快,并且这取代了这些字符串连接:

  1. When is StringBuffer/StringBuilder not implicitly used by the compiler?
  2. What happens when Java Compiler sees many String concatenations in one line?
  3. Java: String concat vs StringBuilder - optimised, so what should I do?
  4. Best practices/performance: mixing StringBuilder.append with String.concat
  5. StringBuilder vs String concatenation in toString() in Java
  6. StringBuilder and StringBuffer
  7. 所以,考虑到我所阅读的内容,StringBuffer通常是更好的选择,这让我想到了几件事:

    1. 编译器何时以及为何选择使用StringBuilder代替AbstractStringBuilder
    2. 如果编译器可以选择使用任何{{1}}实现,那会更有意义吗?

2 个答案:

答案 0 :(得分:7)

您引用的规范的措辞源于旧规范。简单的答案是,在Java 1.5 aka Java 5之前没有StringBuilder。因此,较旧的编译器不必在StringBufferStringBuilder之间进行选择,当时的规范只是建议使用可用的。

使用Java 5,引入了StringBuilder,它不使用synchronized方法,这对于String连接的用例是完美的,因为它是纯粹的本地操作。因此,对于定位1.5或更高版本的编译器,有一个选择(仍然包含规范的单词“或类似技术”),他们将选择StringBuilder,因为没有理由使用StringBuffer 1}}定位1.5或更高时。

答案 1 :(得分:3)

这取决于编译器实现(javac不是唯一的编译器)。但是,对于StringBuffer使用StringBuilder进行这些类型的使用,绝不是一个好例子。它始终是一个使用范围狭窄的对象,StringBuffer的同步不提供任何功能值。

所以简短的回答是没有编译器/应该/曾经使用StringBuffer