私は次のコードを持っています:
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?
デフォルトJava-9では、文字列の連結にStringBuilder
はありません。 invokedynamic
を介してどのように行われるかは実行時の決定です。また、デフォルトのポリシーは_StringBuilder::append
_ポリシーではありません。
詳細を読むこともできます ここ 。
Java-8では、新しいものが作成されます(逆コンパイルされたバイトコードでinvokespecial // Method Java/lang/StringBuilder."<init>":()V
が2回出現するのを見つけるのは非常に簡単です。
また、_append.append...
_についての提案があります。これは_sb.append ... sb.append
_よりもはるかに優れていることに注意してください。 ここ が理由です。
文字列連結の最適化は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>":
)in placeであるため、各反復で新しいStringBuilderが作成されます(ジャストインタイムコンパイラが高度な最適化を実行しない限り)。
これが重大なパフォーマンスの問題になる可能性はほとんどありませんが、まれに、次のように書き換えることで簡単に修正できます。
str.append("|").append(f);
Java 9APIドキュメントによる
実装上の注意:
文字列連結演算子の実装は、コンパイラが最終的にJava™言語仕様に準拠している限り、Javaコンパイラの裁量に任されます。たとえば、javacコンパイラは演算子を実装できます。 JDKのバージョンに応じて、StringBuffer、StringBuilder、またはJava.lang.invoke.StringConcatFactoryを使用します。文字列変換の実装は通常、メソッドtoStringを介して行われ、Objectによって定義され、Javaのすべてのクラスに継承されます。
これによると、あなたの場合、反復ごとに新しい文字列ビルダーが作成されます。したがって、ここで何人かの人々が述べているように、以下のコードを使用することはより最適化されています
append("|").append(f)
APIドキュメントを見つけることができます ここ