JavaのtoString()におけるStringBuilderとStringの連結


925

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?に切り替えますか?


40
どの時点でStringBuilderに切り替えますか?メモリまたはパフォーマンスに影響を与える場合。それともそうなのか。あなたが本当にこれを2、3の文字列に対して一度だけしているなら、心配する必要はありません。しかし、何度も繰り返し使用する場合は、StringBuilderを使用したときに測定可能な違いが見られるはずです。
ewall、2009年

パラメータの100の平均は何ですか?
Asif Mushtaq 2016年

2
@UnKnown 100はStringBuilderの初期サイズです
区切り文字なし'15年

@nonsequitorしたがって、最大文字数は100になりますか?
Asif Mushtaq 2016年

10
@Unknownは初期サイズだけでなく、処理する文字列のおおよそのサイズがわかっている場合は、事前StringBuilderに割り当てるサイズを指定できます。そうでない場合は、スペースが足りなくなった場合に、サイズを2倍にする必要があります。char[]次に新しいアレイがデータをコピーします-これはコストがかかります。サイズを指定することでチートでき、この配列を作成する必要はありません。そのため、文字列が100文字以下になると思われる場合は、StringBuilderをそのサイズに設定でき、内部で拡張する必要はありません。
ノンセクティア2016年

回答:


964

バージョン1のほうが短いので、コンパイラーは実際にはバージョン2に変換しますが、パフォーマンスの違いはありません。

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

ループで連結しているポイントで-コンパイラがStringBuilderそれ自体で置換できない場合が通常です。


19
これは事実ですが、言語リファレンスには、これはオプションであるとも記載されています。実際、JRE 1.6.0_15で簡単なテストを行っただけで、逆コンパイルされたクラスにコンパイラの最適化は見られませんでした。
ブルーノコンデ09/10/07

37
質問のコード(JDK 1.6.0_16でコンパイル)を試してみたところ、期待どおりの最適化が見つかりました。私は現代のすべてのコンパイラがそれを行うと確信しています。
マイケルボルグ

22
あなたは正しいです。バイトコードを見ると、StringBuilderの最適化がはっきりとわかります。私は逆コンパイラを使用していましたが、なんとなく、それはconcatに変換し直しています。+1
ブルーノコンデ09/10/10/7

80
死んだ馬を破ったが、スペックで表現するものではない。To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.そこにキーワードがあること5月。これは正式にオプションであることを考えると(ほとんどの場合は実装されています)、自分自身を保護すべきではありませんか?
Lucas

93
@ルーカス:いいえ、できません。コンパイラがその最適化を実行しないことを決定した場合、それは価値がないためです。99%の場合、コンパイラはどの最適化が価値があるかをよりよく知っているので、経験則として、開発者は干渉すべきではありません。もちろん、状況他の1%に該当する可能性がありますが、それは(慎重な)ベンチマークによってのみ確認できます。
sleske 2012

255

重要なのは、単一の連結をすべて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();

3
はい、StringBuilderはStringオブジェクトを何度も再作成する必要はありません。
Olga

124
Dammit私はこれら2つの関数を使用して、作業中の大きな文字列をテストしました。6.51分vs 11秒
user1722791

1
ちなみに、result += s;最初の例でも使用できます
RAnders00

1
このステートメントによっていくつのオブジェクトが作成されますか?"{a:"+ a + ", b:" + b + ", c: " + c +"}";
Asif Mushtaq 2016年

1
文字列str =(a == null)のようなものはどうですか?null:a '+(b == null)?null:b '+(c == null)?c:c '+ ...; ?これにより、最適化が行われなくなりますか?
amitfr

75

私は好む:

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

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

私は考えていない、あなたが非常に高い繰返し回数でループ内でそれを使用しない限り、スピードのためにこれを最適化し、パフォーマンスの差を測定しました。

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


64
それは実際にはより長く、より多くのシンボルを含み、変数がシーケンス外のテキストを持っています。
トム・ホーティン-

4
それでは、他のアプローチよりも読みにくいと思いますか?
2009年

3
変数を追加する方が簡単なので、これを書くことを好みますが、特に引数の数が多くなるため、読みやすくなるとは思いません。また、異なる時間にビットを追加する必要がある場合は機能しません。
Alex Feinman、

78
(私にとって)読みにくいようです。次に、{...}とパラメータの間を前後にスキャンする必要があります。
スティーブクオ

10
パラメータの1つが次の場合は安全であるため、この形式をおnull
勧めし

74

ほとんどの場合、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番目の方法は高速ですが、読みにくく、保守が難しくなります。私が言ったように、あなたの特定のケースではおそらく違いを見ることはないでしょう。


.concat()を忘れないでください。元の投稿の例のように短い文字列を使用する場合、経過時間は10〜18ミリ秒であり、無視できると思います。
Droo

9
あなたが正しい+=とはいえ、元の例はのシーケンスでした+が、コンパイラはこれを単一のstring.concat呼び出しに変換します。結果は適用されません。
ブリンディ

1
@Blindy&Droo:-どちらも正しいです。ループルーチンが実行されるたびに+ =が新しいオブジェクトを作成するため、このようなシナリオで.concateを使用するのが最善の回避策です。
perilbrain、2011

3
彼のtoString()がループで呼び出されないことを知っていますか?
Omry Yadan 2013

この例を試して速度をテストしました。だから私の結果は次のとおりです:遅い経過29672 ms; 15 msの高速経過。答えは明白です。ただし、100回の反復の場合、時間は同じです-0ミリ秒。500反復の場合-16ミリ秒と0ミリ秒。等々。
Ernestas Gruodis 2013

28

Appendを使用するか、+を使用するかについても、上司と衝突しました。Appendを使用しているためです(新しいオブジェクトが作成されるたびに、私はまだ言うことがわかりません)。だから私はいくつかのR&Dを行うことを考えました.Michael Borgwardtの説明は大好きですが、将来誰かが本当に知っておく必要がある場合は説明を見せたかっただけです。

/**
 *
 * @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つだけ作成されます。


27

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

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

2つの例外:

  • マルチスレッド環境:StringBuffer
  • ループでの連結:StringBuilder / StringBuffer

22

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

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

Java:

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を使用して文字列を追加する

Java:

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

ただし、少し目立つ違いがあります。最初のケースでは、+が使用された場合、StringBuilderforループの反復ごとにnew が作成され、生成された結果はtoString()呼び出し(29〜41)を実行することによって格納されます。したがって+forループ内で演算子を使用しているときに本当に必要のない中間文字列を生成しています。


1
これはOracle JDKまたはOpenJDKですか?
Christophe Roussy

12

Java 9では、バージョン1はinvokedynamic呼び出しに変換されるため、より高速になります。詳細については、JEP-280を参照してください。

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


9

パフォーマンス上の理由から、+=String連結)の使用はお勧めしません。その理由は次のとおりです。Java Stringは不変であり、新しい連結が行われるたびに新しいものStringが作成されます(新しいものは、文字列プールに既にある古いものとは異なるフィンガープリントを持っています)。新しい文字列を作成すると、GCに負荷がかかり、プログラムの速度が低下します。オブジェクトの作成にはコストがかかります。

以下のコードは、それをより実用的かつ明確にする必要があります。

public static void main(String[] args) 
{
    // warming up
    for(int i = 0; i < 100; i++)
        RandomStringUtils.randomAlphanumeric(1024);
    final StringBuilder appender = new StringBuilder();
    for(int i = 0; i < 100; i++)
        appender.append(RandomStringUtils.randomAlphanumeric(i));

    // testing
    for(int i = 1; i <= 10000; i*=10)
        test(i);
}

public static void test(final int howMany) 
{
    List<String> samples = new ArrayList<>(howMany);
    for(int i = 0; i < howMany; i++)
        samples.add(RandomStringUtils.randomAlphabetic(128));

    final StringBuilder builder = new StringBuilder();
    long start = System.nanoTime();
    for(String sample: samples)
        builder.append(sample);
    builder.toString();
    long elapsed = System.nanoTime() - start;
    System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);

    String accumulator = "";
    start = System.nanoTime();
    for(String sample: samples)
        accumulator += sample;
    elapsed = System.nanoTime() - start;
    System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);

    start = System.nanoTime();
    String newOne = null;
    for(String sample: samples)
        newOne = new String(sample);
    elapsed = System.nanoTime() - start;
    System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}

実行の結果を以下に報告します。

builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us

builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us

builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us

builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us

builder - 10000 - elapsed: 3364us 
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us

1つの連結の結果を考慮せず(JITはまだその仕事をしていません)、10回の連結でもパフォーマンスのペナルティは関連しています。何千もの連結に対して、その違いは非常に大きくなります。

この非常に迅速な実験から学んだ教訓(上記のコードで簡単に再現可能):+=いくつかの連結が必要な非常に基本的なケース(たとえ新しい文字列の作成はとにかくコストがかかり、 GC)。


9

以下の例をご覧ください。

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str = str.concat(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case2() {
    System.out.println("[str1+=str2]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str += UUID.randomUUID() + "---";
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case3() {
    System.out.println("[str1.append(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    StringBuilder str = new StringBuilder("");
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str.append(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");

}

static void saveTime(List<Long> executionTimes, long startTime) {
    executionTimes.add(System.currentTimeMillis() - startTime);
    if (executionTimes.size() % CALC_AVG_EVERY == 0) {
        out.println("average time for " + executionTimes.size() + " concatenations: "
                + NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
                + " ms avg");
        executionTimes.clear();
    }
}

出力:

バージョンのJavaバイトコード:8
のjava.version:1.8.0_144
[str1.concat(STR2)]
10000個の連結のための平均時間:平均0.096ミリ
平均0.185 MS:10000個の連結のための平均時間
平均0.327 MS:10000個の連結のための平均時間
の平均時間を10000個の連結:平均0.501ミリ
10000個の連結のための平均時間:0.656ミリ秒avgを
195万で:長さの文字列を作成17745 MS
[0009 + = STR2]
平均0.21 MS:10000個の連結のための平均時間
平均0.652 MS:10000個の連結のための平均時間
の平均時間を10000連結:
平均1.129ミリ秒、10000連結の平均時間:1.727ミリ秒
10000連結の平均時間:2.302 ms平均
作成された長さの文字列:600000ミリ秒で1950000
[str1.append(str2)]
10000連結の
平均時間:0.002ミリ秒10000連結の平均時間:0.002ミリ秒
10000連結の平均時間:平均0.002ミリ
平均0.002 MS:10000個の連結のための平均時間
10000個の連結のための平均時間:0.002ミリ秒AVGは
で195万:長さの文字列を作成し、100ミリ秒

文字列の長さが増えると、連結時間が長くなります。
それがStringBuilder絶対に必要な場所です。
ご覧のとおり、連結:UUID.randomUUID()+"---"は実際には時間に影響しません。

PS:JavaStringBuilderをいつ使用するかは、実際にはこれと同じです。
この質問は、toString()ほとんどの場合、巨大な文字列の連結を実行しないことについて話します。


2019アップデート

java8時代以来、状況は少し変わっています。現在(java13)の連結時間は+=とほぼ同じようstr.concat()です。ただし、StringBuilder連結時間は一定です。(上記の元の投稿は、より詳細な出力を追加するために少し編集されました)

javaバイトコードバージョン:13
java.version:13.0.1
[str1.concat(str2)]
10000連結の平均時間:0.047 ms平均
10000連結の平均時間:0.1 ms
平均時間10000連結の
平均:0.17 ms 平均時間10000連結:平均
平均時間0.255 ms 10000連結:平均0.336 ms
作成された長さの文字列:9147ミリ秒で 1950000
[str1 + = str2]
10000連結の平均時間:0.037ミリ秒
10000連結の平均時間:0.097ミリ秒の
平均時間10000連結:
平均0.249 ms 平均時間10000連結:0.298 ms平均
10000連結の平均時間:0.326ミリ秒平均
作成された長さの文字列:100000 ミリ秒の1950000
[str1.append(str2)]
10000連結の
平均時間:0.001ミリ秒10000連結の平均時間:0.001ミリ秒平均
10000連結の平均時間:平均0.001ミリ
平均0.001 MS:10000個の連結のための平均時間
10000個の連結のための平均時間:0.001ミリ秒AVGは
で195万:長さの文字列を作成した43ミリ秒

注目に値するbytecode:8/java.version:13組み合わせにも比べて優れたパフォーマンス上の利点がありますbytecode:8/java.version:8


これは受け入れられる答えである必要があります..それは、連結またはStringBuilderの選択を決定する文字列ストリームのサイズに依存します
user1428716

@ user1428716 FYI:回答が更新され、結果java13が異なる結果になりました。しかし、主な結論は変わらないと思います。
マリノス

7

Apache Commons-Langには、非常に使いやすいToStringBuilderクラスがあります。これは、append-logicを処理するだけでなく、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のスタイルをカスタマイズすることもできます。


4

toStringメソッドをできるだけ読みやすくしてください!

この本の唯一の例外は、大量のリソースを消費していることを私に証明できる場合です:)(はい、これはプロファイリングを意味します)

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


3

現在のコンパイラでStringBuilderを使用する必要があるかどうかについては、いくつかの議論があるようです。だから私は私の2セントの経験を与えると思った。

1 JDBC万件のレコードの結果セットがあります(そうです、それらすべてを1つのバッチで必要とします)。+演算子を使用すると、私のマシンでを使用して約5分かかりますJava 1.8。使い方はstringBuilder.append("")同じクエリのための秒未満かかります。

そのため、違いは非常に大きくなります。ループ内StringBuilderははるかに高速です。


2
議論はループの外でそれを使用することについてだと思います。ループ内で使用する必要があるコンセンサスがあると思います。
ジョーダン

2

文字列はJavaでは不変であるため、文字列のまったく新しいコピーを作成する必要があるため、「+」を使用したパフォーマンスに優れた文字列連結はコストが高くなります。これは、連結が非常に頻繁に行われる場合(ループ内など)に特に役立ちます。以下は、私がそのようなことを行おうとするときに私のIDEAが提案するものです。

ここに画像の説明を入力してください

一般的なルール:

  • 単一の文字列割り当て内では、文字列連結を使用しても問題ありません。
  • ループして文字データの大きなブロックを作成する場合は、StringBufferを使用してください。
  • 文字列で+ =を使用すると、常にStringBufferを使用するよりも効率が低下するため、警告音が鳴るはずです。ただし、特定のケースでは、読みやすさの問題と比較して、得られる最適化が無視できるため、常識に従ってください。

ここで素敵なジョンスキートのブログこのトピックの周りには。


2
複数のスレッドからの同期アクセスが絶対に必要でない限り、StringBufferを使用しないでください。それ以外の場合は、同期されていないオーバーヘッドが少ないStringBuilderを優先します。
Erki der Loony、


1

これは私がJava8でチェックしたものです

  • 文字列連結の使用
  • StringBuilderの使用

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

多数の文字列に文字列連結を使用している場合、それは本当に悪夢です。

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms

-1

StringBuilderの追加アプローチを使用する必要があると思います。理由:

  1. String連結は、毎回新しい文字列オブジェクトを作成します(Stringは不変オブジェクトであるため)ため、3つのオブジェクトが作成されます。

  2. 文字列ビルダーを使用すると、1つのオブジェクトのみが作成され[StringBuilderは変更可能]、さらに文字列が追加されます。


なぜこの回答は反対投票ですか?docs.oracle.com/javase/8/docs/api/java/util/stream/…-可変リダクション
user1428716

-4

そのような単純な文字列の場合、私は使いたい

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

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


9
JLSでは '+'をStringBuilderに変換できるため、concat()を使用すると '+'よりもパフォーマンスが低下する可能性が高く、ほとんどすべてのJVMがそうするか、より効率的な代替手段を使用します。同じことが当てはまる可能性は低いです。この例では、少なくとも1つの完全な中間文字列を作成して破棄する必要があるconcat。
Lawrence Dol、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.