Pertanyaan StringBuilder vs Rangkaian string di toString () di Java


Diberikan 2 toString() implementasi di bawah ini, yang mana yang lebih disukai:

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

atau

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

?

Lebih penting lagi, mengingat kami hanya memiliki 3 properti itu mungkin tidak membuat perbedaan, tetapi pada titik mana Anda akan beralih dari + berdamai StringBuilder?


766
2017-10-07 15:44


asal


Jawaban:


Versi 1 lebih disukai karena lebih pendek dan kompiler pada kenyataannya akan mengubahnya menjadi versi 2 - tidak ada perbedaan kinerja sama sekali.

Lebih penting lagi, kami hanya punya 3   properti itu mungkin tidak membuat   perbedaan, tetapi pada titik mana Anda   beralih dari concat ke builder?

Pada titik di mana Anda menggabungkan dalam satu lingkaran - itu biasanya ketika compiler tidak dapat menggantikan StringBuilder dengan sendirinya.


804
2017-10-07 15:51



Kuncinya adalah apakah Anda menulis rentetan tunggal di satu tempat atau mengumpulkannya dari waktu ke waktu.

Untuk contoh yang Anda berikan, tidak ada gunanya secara eksplisit menggunakan StringBuilder. (Lihatlah kode yang dikompilasi untuk kasus pertama Anda.)

Tetapi jika Anda sedang membangun string, mis. di dalam satu lingkaran, gunakan StringBuilder.

Untuk memperjelas, dengan asumsi bahwa bigArray berisi ribuan string, kode seperti ini:

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

sangat membuang waktu dan memori dibandingkan dengan:

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

204
2017-10-07 15:47



Aku lebih memilih:

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

... karena pendek dan mudah dibaca.

saya akan tidak optimalkan ini untuk kecepatan kecuali Anda menggunakannya di dalam lingkaran dengan jumlah ulangan yang sangat tinggi dan telah mengukur perbedaan kinerja.

Saya setuju, bahwa jika Anda harus mengeluarkan banyak parameter, formulir ini bisa membingungkan (seperti salah satu komentar katakan). Dalam hal ini saya beralih ke bentuk yang lebih mudah dibaca (mungkin menggunakan ToStringBuilder dari apache-commons - diambil dari jawaban matt b) dan abaikan kinerja lagi.


64
2017-10-07 15:47



Dalam banyak kasus, Anda tidak akan melihat perbedaan nyata antara dua pendekatan, tetapi mudah untuk membangun skenario terburuk seperti ini:

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

Outputnya adalah:

slow elapsed 11741 ms
fast elapsed 7 ms

Masalahnya adalah bahwa untuk + = menambahkan ke string merekonstruksi string baru, sehingga biaya sesuatu yang linear dengan panjang string Anda (jumlah keduanya).

Jadi - untuk pertanyaan Anda:

Pendekatan kedua akan lebih cepat, tetapi kurang bisa dibaca dan lebih sulit untuk dipertahankan. Seperti yang saya katakan, dalam kasus spesifik Anda, Anda mungkin tidak akan melihat perbedaannya.


56
2017-10-07 15:58



Saya juga pernah bentrok dengan atasan saya tentang fakta apakah akan menggunakan append atau +. Seperti yang mereka gunakan Append (saya masih tidak dapat mengetahuinya seperti yang mereka katakan setiap kali objek baru dibuat). Jadi saya berpikir untuk melakukan R & D. Meskipun saya suka penjelasan Michael Borgwardt tetapi hanya ingin menunjukkan penjelasan jika seseorang benar-benar perlu tahu di masa depan.

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

dan pembongkaran kelas di atas keluar sebagai

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

Dari dua kode di atas Anda dapat melihat Michael benar. Dalam setiap kasus hanya satu objek SB dibuat.


26
2017-12-21 20:29



Sejak Java 1.5, simpel satu baris sederhana dengan "+" dan StringBuilder.append () menghasilkan bytecode yang sama persis.

Jadi demi keterbacaan kode, gunakan "+".

2 pengecualian:

  • lingkungan multithread: StringBuffer
  • Rangkaian dalam loop: StringBuilder / StringBuffer

23
2018-04-16 14:14



Menggunakan versi terbaru Java (1.8) pembongkaran (javap -c) menunjukkan optimasi yang diperkenalkan oleh compiler. + demikian juga sb.append() akan menghasilkan kode yang sangat mirip. Namun, itu akan bermanfaat untuk memeriksa perilaku jika kita menggunakan + dalam for loop.

Menambahkan string menggunakan + dalam for loop

Jawa:

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

ByteCode :(for kutipan loop)

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

Menambahkan string menggunakan stringbuilder.append

Jawa:

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

ByteCdoe :(for kutipan loop)

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

Ada sedikit perbedaan mencolok meskipun. Dalam kasus pertama, di mana + telah digunakan, baru StringBuilder dibuat untuk masing-masing untuk iterasi loop dan hasil yang dihasilkan disimpan dengan melakukan toString() panggilan (29 hingga 41). Jadi Anda menghasilkan String menengah yang benar-benar Anda tidak perlu saat menggunakan + operator di for lingkaran.


19
2018-05-26 07:15