Оптимизирована ли конкатенация строк для использования существующих 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?
Ответы
Ответ 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 здесь