Подтвердить что ты не робот

Оптимизирована ли конкатенация строк для использования существующих StringBuilders?

У меня есть следующий код:

StringBuilder str = new StringBuilder("foo");
for(Field f : fields){
    str.append("|" + f);
}
str.append("|" + bar);
String result = str.toString();

Я знаю, что компиляция оптимизирует конкатенацию строк "|" + f и заменяет ее на StringBuilder. Однако будет ли создан новый StringBuilder или существующий str будет использоваться в Java 8? Как насчет Java 9?

4b9b3361

Ответ 1

По умолчанию в java-9 не будет StringBuilder для конкатенации строк - это решение времени выполнения, которое оно выполнило с помощью invokedynamic. И политика по умолчанию не является StringBuilder::append.

Вы также можете прочитать здесь.

В java-8 будет создан новый (очень легко заметить два вхождения invokespecial // Method java/lang/StringBuilder."<init>":()V в декомпилированном байт-коде.

Кроме того, у вас есть предложение о append.append...; просто заметите, что это намного лучше, чем sb.append ... sb.append, а здесь.

Ответ 2

Поскольку оптимизация конкатенации строк выполняется компилятором Java, вы можете увидеть, что она делает, декомпилируя код байта:

$ cat Test.java
interface Field {}

public class Test {

    static String toString(Field[] fields, Object bar) {
        StringBuilder str = new StringBuilder("foo");
        for(Field f : fields){
            str.append("|" + f);
        }
        str.append("|" + bar);
        return str.toString();
    }
}
$ javac Test.java
$ javap -c Test.class
Compiled from "Test.java"
public class stackoverflow.Test {
  public stackoverflow.Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  static java.lang.String toString(stackoverflow.Field[], java.lang.Object);
    Code:
       0: new           #16                 // class java/lang/StringBuilder
       3: dup
       4: ldc           #18                 // String foo
       6: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
       9: astore_2
      10: aload_0
      11: dup
      12: astore        6
      14: arraylength
      15: istore        5
      17: iconst_0
      18: istore        4
      20: goto          53
      23: aload         6
      25: iload         4
      27: aaload
      28: astore_3
      29: aload_2
      30: new           #16                 // class java/lang/StringBuilder
      33: dup
      34: ldc           #23                 // String |
      36: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      39: aload_3
      40: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      43: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      46: invokevirtual #32                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      49: pop
      50: iinc          4, 1
      53: iload         4
      55: iload         5
      57: if_icmplt     23
      60: aload_2
      61: new           #16                 // class java/lang/StringBuilder
      64: dup
      65: ldc           #23                 // String |
      67: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      70: aload_1
      71: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      74: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      77: invokevirtual #32                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      80: pop
      81: aload_2
      82: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      85: areturn
}

Как вы можете видеть, код вызывает конструкторы StringBuilder (Method java/lang/StringBuilder."<init>":) в трех местах, поэтому на каждой итерации будут созданы новые StringBuilders (если только компилятор "точно в момент времени" не выполняет оптимизацию).

Это очень маловероятно, чтобы быть серьезной проблемой с производительностью, но в маловероятном случае вы можете легко исправить это, переписав

str.append("|").append(f);

Ответ 3

В соответствии с документацией API Java 9

Примечание по реализации:

Реализация оператора конкатенации строк остается на усмотрение компилятора Java, если компилятор в конечном итоге соответствует спецификации языка Java ™. Например, компилятор javac может реализовать оператор с StringBuffer, StringBuilder или java.lang.invoke.StringConcatFactory в зависимости от версии JDK. Реализация преобразования строк обычно осуществляется с помощью метода toString, определенного Object и унаследованного всеми классами на Java.

В соответствии с этим он создаст новый конструктор String на каждой итерации в вашем случае. Итак, как упоминалось несколькими людьми здесь, использование приведенного ниже кода более оптимизировано.

append("|").append(f)

Вы можете найти документацию по API здесь