1043

toString()以下の 2 つの実装を考えると、どちらが優先されますか。

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

また

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

?

さらに重要なことは、プロパティが 3 つしかないことを考えると、違いはないかもしれませんが、どの時点で+concat から に 切り替えStringBuilderますか?

4

19 に答える 19

1074

バージョン 1 の方が短く、実際にはコンパイラによってバージョン 2 に変換されるため、バージョン 1 が推奨されます。パフォーマンスの違いはまったくありません。

さらに重要なことは、プロパティが 3 つしかないことを考えると、違いはないかもしれませんが、どの時点で concat からビルダーに切り替えるのでしょうか?

ループ内で連結している時点で、通常、コンパイラがStringBuilderそれ自体で置換できないときです。

于 2009-10-07T15:51:05.743 に答える
287

重要なのは、単一の連結をすべて 1 か所に書いているのか、それとも時間をかけて蓄積しているかということです。

あなたが示した例では、明示的に StringBuilder を使用しても意味がありません。(最初のケースのコンパイル済みコードを見てください。)

ただし、ループ内などで文字列を作成する場合は、StringBuilder を使用してください。

明確にするために、 hugeArray に何千もの文字列が含まれていると仮定すると、次のようにコーディングします。

...
String result = "";
for (String s : hugeArray) {
    result = result + s;
}

次のものと比較して、非常に時間とメモリを浪費します。

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();
于 2009-10-07T15:47:43.453 に答える
87

ほとんどの場合、2 つのアプローチの間に実際の違いは見られませんが、次のような最悪のシナリオを簡単に作成できます。

public class Main
{
    public static void main(String[] args)
    {
        long now = System.currentTimeMillis();
        slow();
        System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");

        now = System.currentTimeMillis();
        fast();
        System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
    }

    private static void fast()
    {
        StringBuilder s = new StringBuilder();
        for(int i=0;i<100000;i++)
            s.append("*");      
    }

    private static void slow()
    {
        String s = "";
        for(int i=0;i<100000;i++)
            s+="*";
    }
}

出力は次のとおりです。

slow elapsed 11741 ms
fast elapsed 7 ms

問題は、文字列に += を追加すると新しい文字列が再構築されるため、文字列の長さ (両方の合計) に比例してコストがかかることです。

だから - あなたの質問に:

2 番目の方法は高速ですが、読みにくく、保守が困難です。私が言ったように、あなたの特定のケースでは、おそらく違いはわかりません。

于 2009-10-07T15:58:15.880 に答える
75

私が好む:

String.format( "{a: %s, b: %s, c: %s}", a, b, c );

...短くて読みやすいからです。

繰り返し回数が非常に多いループ内で使用し、パフォーマンスの違いを測定しない限り、これを速度のために最適化することはありません。

多くのパラメーターを出力する必要がある場合、このフォームは混乱する可能性があることに同意します(コメントの1つが言うように)。この場合、より読みやすい形式に切り替え (おそらくapache-commons のToStringBuilderを使用 - マット b の回答から取得)、パフォーマンスを再び無視します。

于 2009-10-07T15:47:47.277 に答える
31

Java 1.5 以降、「+」と StringBuilder.append() を使用した単純な 1 行の連結で、まったく同じバイトコードが生成されます。

したがって、コードを読みやすくするために、「+」を使用してください。

2 つの例外:

  • マルチスレッド環境: StringBuffer
  • ループ内の連結: StringBuilder/StringBuffer
于 2012-04-16T14:14:49.637 に答える
29

また、上司とは、appendと+のどちらを使用するかについて衝突しました。彼らは、Appendを使用しているためです(新しいオブジェクトが作成されるたびに、彼らが言うように、私はまだ理解できません)。そこで、研究開発を考えました。マイケル・ボルグワードの説明は大好きですが、将来誰かが本当に知る必要がある場合は、説明を示したかっただけです。

/**
 *
 * @author Perilbrain
 */
public class Appc {
    public Appc() {
        String x = "no name";
        x += "I have Added a name" + "We May need few more names" + Appc.this;
        x.concat(x);
        // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
        //System.out.println(x);
    }

    public void Sb() {
        StringBuilder sbb = new StringBuilder("no name");
        sbb.append("I have Added a name");
        sbb.append("We May need few more names");
        sbb.append(Appc.this);
        sbb.append(sbb.toString());
        // System.out.println(sbb.toString());
    }
}

上記のクラスの分解は次のようになります

 .method public <init>()V //public Appc()
  .limit stack 2
  .limit locals 2
met001_begin:                                  ; DATA XREF: met001_slot000i
  .line 12
    aload_0 ; met001_slot000
    invokespecial java/lang/Object.<init>()V
  .line 13
    ldc "no name"
    astore_1 ; met001_slot001
  .line 14

met001_7:                                      ; DATA XREF: met001_slot001i
    new java/lang/StringBuilder //1st object of SB
    dup
    invokespecial java/lang/StringBuilder.<init>()V
    aload_1 ; met001_slot001
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    ldc "I have Added a nameWe May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    aload_0 ; met001_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    astore_1 ; met001_slot001
  .line 15
    aload_1 ; met001_slot001
    aload_1 ; met001_slot001
    invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
    pop
  .line 18
    return //no more SB created
met001_end:                                    ; DATA XREF: met001_slot000i ...

; ===========================================================================

;met001_slot000                                ; DATA XREF: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+6w ...
    .var 1 is x Ljava/lang/String; from met001_7 to met001_end
  .end method
;44-1=44
; ---------------------------------------------------------------------------


; Segment type: Pure code
  .method public Sb()V //public void Sb
  .limit stack 3
  .limit locals 2
met002_begin:                                  ; DATA XREF: met002_slot000i
  .line 21
    new java/lang/StringBuilder
    dup
    ldc "no name"
    invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    astore_1 ; met002_slot001
  .line 22

met002_10:                                     ; DATA XREF: met002_slot001i
    aload_1 ; met002_slot001
    ldc "I have Added a name"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 23
    aload_1 ; met002_slot001
    ldc "We May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 24
    aload_1 ; met002_slot001
    aload_0 ; met002_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    pop
  .line 25
    aload_1 ; met002_slot001
    aload_1 ; met002_slot001
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 28
    return
met002_end:                                    ; DATA XREF: met002_slot000i ...


;met002_slot000                                ; DATA XREF: Sb+25r
    .var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001                                ; DATA XREF: Sb+9w ...
    .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
  .end method
;96-49=48
; ---------------------------------------------------------------------------

上記の2つのコードから、Michaelが正しいことがわかります。いずれの場合も、SBオブジェクトは1つだけ作成されます。

于 2011-12-21T20:29:05.077 に答える
23

最新バージョンの Java(1.8) を使用すると、disassembly( javap -c) は、コンパイラによって導入された最適化を示します。+同様sb.append()に、非常によく似たコードが生成されます。+ただし、 for ループで使用している場合は、動作を調べる価値があります。

for ループで + を使用して文字列を追加する

ジャワ:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

ByteCode:(forループ抜粋)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

stringbuilder.append を使用して文字列を追加する

ジャワ:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

ByteCdoe:(forループ抜粋)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

ただし、少し明白な違いがあります。使用された最初のケ​​ースでは、 for ループの反復ごとに+newが作成され、生成された結果は呼び出し (29 から 41) を実行して格納されます。そのため、ループで演算子を使用している間は本当に必要のない中間文字列を生成しています。StringBuildertoString()+for

于 2015-05-26T07:15:27.400 に答える
13

invokedynamicJava 9 では、バージョン 1 の方がcallに変換されるため、高速になるはずです。詳細はJEP-280にあります:

アイデアは、StringBuilder のアペンド ダンス全体を、連結が必要な値を受け入れる java.lang.invoke.StringConcatFactory への単純な invokedynamic 呼び出しに置き換えることです。

于 2017-04-03T00:53:52.003 に答える
7

Apache Commons-Lang には、非常に使いやすいToStringBuilderクラスがあります。これは、追加ロジックの処理と、toString の外観の書式設定の両方をうまく処理します。

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

のような出力を返しますcom.blah.YourClass@abc1321f[a=whatever, b=foo]

または、チェーンを使用してより凝縮された形式で:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

または、リフレクションを使用してクラスのすべてのフィールドを含める場合:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

必要に応じて、ToString のスタイルをカスタマイズすることもできます。

于 2009-10-07T16:09:57.730 に答える
4

toStringメソッドをできるだけ読みやすくします。

私の本でのこれの唯一の例外は、それがかなりのリソースを消費していることを私に証明できるかどうかです:)(はい、これはプロファイリングを意味します)

また、Java 5コンパイラは、以前のバージョンのJavaで使用されていた手書きの「StringBuffer」アプローチよりも高速なコードを生成することに注意してください。「+」を使用すると、これと将来の拡張機能は無料で提供されます。

于 2009-10-07T19:05:23.477 に答える
1

コレクションを反復処理して StringBuilder を使用する場合は、 Apache Commons LangStringUtils.join() (さまざまなフレーバー)を確認することをお勧めします。

パフォーマンスに関係なく、StringBuilders と for ループを何百万回も作成する必要がなくなります。

于 2009-10-07T16:02:37.307 に答える
-4

そのような単純な文字列の場合、私は使用することを好みます

"string".concat("string").concat("string");

順番に、文字列を構築する好ましい方法は、StringBuilder、String#concat()、オーバーロードされた + 演算子を使用することです。StringBuilder は、+ 演算子を使用するとパフォーマンスが大幅に低下するのと同じように、大きな文字列を処理する場合にパフォーマンスが大幅に向上します (文字列のサイズが大きくなるにつれて指数関数的に大幅に低下します)。.concat() の使用に関する 1 つの問題は、NullPointerExceptions がスローされる可能性があることです。

于 2009-10-07T16:11:39.040 に答える