Frage StringBuilder vs String-Verkettung in toString () in Java


Angesichts der 2 toString() Implementierungen unten, welche ist bevorzugt:

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

oder

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

?

Was noch wichtiger ist: Wenn wir nur 3 Eigenschaften haben, macht das vielleicht keinen Unterschied, aber an welchem ​​Punkt würden Sie wechseln + concat zu StringBuilder?


766
2017-10-07 15:44


Ursprung


Antworten:


Version 1 ist vorzuziehen, weil sie kürzer und kürzer ist der Compiler wird es tatsächlich in Version 2 verwandeln - Kein Leistungsunterschied überhaupt.

Wichtiger, da wir nur 3 haben   Eigenschaften, die es möglicherweise nicht macht   Unterschied, aber an welchem ​​Punkt tun Sie   Wechsel von Concat zu Builder?

An dem Punkt, wo Sie in einer Schleife verketten - das ist normalerweise, wenn der Compiler nicht ersetzen kann StringBuilder von selbst.


804
2017-10-07 15:51



Der Schlüssel ist, ob Sie eine einzelne Verkettung an einer Stelle schreiben oder im Laufe der Zeit akkumulieren.

Für das Beispiel, das Sie angegeben haben, ist es nicht sinnvoll, StringBuilder explizit zu verwenden. (Schauen Sie sich den kompilierten Code für Ihren ersten Fall an.)

Aber wenn Sie eine Saite bauen, z.B. In einer Schleife verwenden Sie StringBuilder.

Um zu verdeutlichen, dass riangleArray Tausende von Strings enthält, code wie folgt:

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

ist sehr zeit- und speicherverschwendend im Vergleich zu:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();

204
2017-10-07 15:47



Ich bevorzuge:

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

... weil es kurz und lesbar ist.

Ich würde nicht Optimieren Sie dies für die Geschwindigkeit, es sei denn, Sie verwenden es in einer Schleife mit einer sehr hohen Wiederholungszahl und habe den Leistungsunterschied gemessen.

Ich stimme zu, dass, wenn Sie viele Parameter ausgeben müssen, dieses Formular verwirrend werden kann (wie einer der Kommentare sagt). In diesem Fall würde ich zu einer besser lesbaren Form wechseln (vielleicht mit ToStringBuilder von Apache-Commons - aus der Antwort von matt b) und ignorieren die Leistung wieder.


64
2017-10-07 15:47



In den meisten Fällen sehen Sie keinen tatsächlichen Unterschied zwischen den beiden Ansätzen, aber es ist einfach, ein Worst-Case-Szenario wie dieses zu konstruieren:

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+="*";
    }
}

Die Ausgabe ist:

slow elapsed 11741 ms
fast elapsed 7 ms

Das Problem ist, dass wenn man + an eine Zeichenkette anfügt, eine neue Zeichenkette rekonstruiert, so dass es etwas kostet, das linear zur Länge der Zeichenketten ist (Summe aus beiden).

Also - zu deiner Frage:

Der zweite Ansatz wäre schneller, aber weniger lesbar und schwerer zu warten. Wie gesagt, in Ihrem speziellen Fall würden Sie den Unterschied wahrscheinlich nicht sehen.


56
2017-10-07 15:58



Ich hatte auch einen Konflikt mit meinem Chef über die Tatsache, ob append oder + zu verwenden. Wie sie Append verwenden (ich kann immer noch nicht herausfinden, wie sie jedes Mal sagen, ein neues Objekt erstellt wird). Also dachte ich mir, dass ich etwas R & D machen könnte. Obwohl ich Michael Borgwardt Erklärung liebe, aber wollte nur eine Erklärung zeigen, wenn jemand wirklich in Zukunft wissen muss.

/**
 *
 * @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());
    }
}

und Demontage der oben genannten Klasse kommt als heraus

 .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
; ---------------------------------------------------------------------------

Aus den obigen beiden Codes kann man sehen, dass Michael Recht hat. Es wird jeweils nur ein SB-Objekt erstellt.


26
2017-12-21 20:29



Seit Java 1.5 erzeugen einfache Ein-Zeilen-Verkettungen mit "+" und StringBuilder.append () genau denselben Bytecode.

Um die Lesbarkeit des Codes zu gewährleisten, verwenden Sie "+".

2 Ausnahmen:

  • Multithread-Umgebung: StringBuffer
  • Verkettung in Schleifen: StringBuilder / StringBuffer

23
2018-04-16 14:14



Mit der neuesten Version von Java (1.8) wird die Disassemblierung (javap -c) zeigt die vom Compiler eingeführte Optimierung. + auch sb.append() wird sehr ähnlichen Code generieren. Es lohnt sich jedoch, das Verhalten zu überprüfen, wenn wir es verwenden + in einer for-Schleife.

Hinzufügen von Zeichenfolgen mit + in einer for-Schleife

Java:

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

ByteCode :(for Schleifenausschnitt)

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

Strings mit stringbuilder.append hinzufügen

Java:

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

ByteCdoe :(for Schleifenausschnitt)

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

Es gibt ein bisschen krasser Unterschied obwohl. Im ersten Fall, wo + wurde benutzt, neu StringBuilder wird für jede Schleifeniteration erzeugt und das erzeugte Ergebnis wird gespeichert, indem a ausgeführt wird toString() Anruf (29 bis 41). Sie erzeugen also Zwischenfolgen, die Sie während der Verwendung nicht wirklich benötigen + Betreiber in for Schleife.


19
2018-05-26 07:15