web-dev-qa-db-ja.com

文字列の連結は、既存のStringBuilderを使用するように最適化されますか?

私は次のコードを持っています:

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?

23
Anton Krosnev

デフォルトJava-9では、文字列の連結にStringBuilderはありません。 invokedynamicを介してどのように行われるかは実行時の決定です。また、デフォルトのポリシーは_StringBuilder::append_ポリシーではありません

詳細を読むこともできます ここ

Java-8では、新しいものが作成されます(逆コンパイルされたバイトコードでinvokespecial // Method Java/lang/StringBuilder."<init>":()Vが2回出現するのを見つけるのは非常に簡単です。

また、_append.append..._についての提案があります。これは_sb.append ... sb.append_よりもはるかに優れていることに注意してください。 ここ が理由です。

23
Eugene

文字列連結の最適化は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);
8
meriton

Java 9APIドキュメントによる

実装上の注意:

文字列連結演算子の実装は、コンパイラが最終的にJava™言語仕様に準拠している限り、Javaコンパイラの裁量に任されます。たとえば、javacコンパイラは演算子を実装できます。 JDKのバージョンに応じて、StringBuffer、StringBuilder、またはJava.lang.invoke.StringConcatFactoryを使用します。文字列変換の実装は通常、メソッドtoStringを介して行われ、Objectによって定義され、Javaのすべてのクラスに継承されます。

これによると、あなたの場合、反復ごとに新しい文字列ビルダーが作成されます。したがって、ここで何人かの人々が述べているように、以下のコードを使用することはより最適化されています

append("|").append(f)

APIドキュメントを見つけることができます ここ

0
Manindar