StringBuilder vs String concatenation di toString () di Java

925

Dengan 2 toString()implementasi di bawah ini, 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 kita hanya memiliki 3 properti mungkin tidak membuat perbedaan, tetapi pada titik apa Anda akan beralih dari +concat ke StringBuilder?

bukan sequitor
sumber
40
Pada titik apa Anda beralih ke StringBuilder? Ketika itu mempengaruhi memori atau kinerja. Atau kapan mungkin. Jika Anda benar-benar hanya melakukan ini sekali saja, jangan khawatir. Tetapi jika Anda akan melakukannya berulang kali, Anda akan melihat perbedaan yang terukur saat menggunakan StringBuilder.
ewall
apa arti dari 100 dalam parameter?
Asif Mushtaq
2
@UnKnown 100 adalah ukuran awal dari StringBuilder
non sequitor
@nonsequitor Jadi karakter maksimum akan menjadi 100?
Asif Mushtaq
10
@ Diketahui tidak hanya ukuran awal, jika Anda tahu ukuran perkiraan dari string yang Anda hadapi maka Anda dapat mengetahui StringBuilderberapa banyak ukuran untuk mengalokasikan dimuka jika tidak, jika kehabisan ruang, harus menggandakan ukuran dengan membuat char[]array baru kemudian menyalin data - yang mahal. Anda dapat menipu dengan memberikan ukuran dan kemudian tidak perlu untuk pembuatan array ini - jadi jika Anda berpikir string Anda akan ~ 100 char panjangnya maka Anda dapat mengatur StringBuilder ke ukuran itu dan tidak akan perlu meluas secara internal.
non sequitor

Jawaban:

964

Versi 1 lebih disukai karena lebih pendek dan kompiler sebenarnya akan mengubahnya menjadi versi 2 - tidak ada perbedaan kinerja apa pun.

Lebih penting lagi mengingat kita hanya memiliki 3 properti mungkin tidak membuat perbedaan, tetapi pada titik apa Anda beralih dari concat ke builder?

Pada titik di mana Anda menggabungkan dalam satu lingkaran - itu biasanya ketika kompiler tidak dapat menggantikan StringBuilderdengan sendirinya.

Michael Borgwardt
sumber
19
Itu benar tetapi referensi bahasa juga menyatakan bahwa ini opsional. Bahkan, saya hanya melakukan tes sederhana dengan JRE 1.6.0_15 dan saya tidak melihat optimasi kompiler di kelas yang didekompilasi.
Bruno conde
37
Saya Hanya mencoba kode dari pertanyaan (dikompilasi pada JDK 1.6.0_16) dan menemukan optimasi seperti yang diharapkan. Saya cukup yakin semua kompiler modern akan melakukannya.
Michael Borgwardt
22
Anda benar. Melihat bytecode saya dapat dengan jelas melihat optimasi StringBuilder. Saya menggunakan decompiler dan, entah bagaimana, itu mengkonversi kembali ke concat. +1
bruno conde
80
Bukan untuk mengalahkan kuda mati, tetapi kata-kata dalam spec adalah: 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.Kata kuncinya mungkin ada . Mengingat ini secara resmi opsional (walaupun kemungkinan besar dilaksanakan) tidakkah kita harus melindungi diri kita sendiri?
Lucas
93
@Lucas: Tidak, seharusnya tidak. Jika kompiler memutuskan untuk tidak melakukan optimasi itu, itu karena itu tidak layak. Dalam 99% kasus, kompiler lebih tahu optimasi mana yang layak, sehingga sebagai aturan praktis, dev tidak boleh mengganggu. Tentu saja, situasi Anda mungkin jatuh ke dalam 1% lainnya, tetapi itu hanya dapat diperiksa dengan benchmarking (hati-hati).
sleske
255

Kuncinya adalah apakah Anda menulis satu rangkaian 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 membangun string misalnya di dalam loop, gunakan StringBuilder.

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

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

sangat boros waktu dan memori dibandingkan dengan:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();
joel.neely
sumber
3
Ya, StringBuilder tidak perlu membuat ulang objek String berulang kali.
Olga
124
Sialan, saya menggunakan 2 fungsi itu untuk menguji string besar yang sedang saya kerjakan. 6.51min vs 11secs
user1722791
1
Omong-omong, Anda dapat menggunakan result += s;juga (dalam contoh pertama)
RAnders00
1
berapa banyak objek yang akan dibuat oleh pernyataan ini? "{a:"+ a + ", b:" + b + ", c: " + c +"}";
Asif Mushtaq
1
Bagaimana dengan sesuatu seperti: String str = (a == null)? null: a '+ (b == null)? null: b '+ (c == null)? c: c '+ ...; ? Apakah itu akan mencegah pengoptimalan terjadi?
amitfr
75

Saya lebih memilih:

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

... karena pendek dan mudah dibaca.

Saya tidak akan mengoptimalkan ini untuk kecepatan kecuali jika Anda menggunakannya di dalam lingkaran dengan jumlah pengulangan yang sangat tinggi dan telah mengukur perbedaan kinerja.

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

tangens
sumber
64
Ini sebenarnya lebih lama, mengandung lebih banyak simbol dan memiliki variabel teks di luar urutan.
Tom Hawtin - tackline
4
Jadi, menurut Anda apakah itu kurang dapat dibaca daripada salah satu dari pendekatan lainnya?
tangens
3
Saya lebih suka menulis ini, karena lebih mudah untuk menambahkan lebih banyak variabel, tapi saya tidak yakin itu lebih mudah dibaca - terutama karena jumlah argumen semakin besar. Ini juga tidak berfungsi saat Anda perlu menambahkan bit pada waktu yang berbeda.
Alex Feinman
78
Tampaknya lebih sulit dibaca (untuk saya). Sekarang saya harus memindai bolak-balik antara {...} dan parameternya.
Steve Kuo
10
Saya lebih suka formulir ini, karena aman jika salah satu parameternyanull
rds
74

Dalam kebanyakan kasus, Anda tidak akan melihat perbedaan nyata antara kedua pendekatan, tetapi mudah untuk membuat skenario kasus 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 linier dengan panjang string Anda (jumlah keduanya).

Jadi - untuk pertanyaan Anda:

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

Omry Yadan
sumber
Jangan lupa tentang .concat (). Saya akan menduga waktu yang telah berlalu untuk berada di mana saja dari 10 hingga 18 ms membuatnya diabaikan ketika menggunakan string pendek seperti contoh posting asli.
Droo
9
Meskipun Anda benar +=, contoh aslinya adalah urutan +, yang diubah oleh kompiler menjadi satu string.concatpanggilan. Hasil Anda tidak berlaku.
Blindy
1
@ Blindy & Droo: - Anda berdua benar. Menggunakan .concate dalam skenario seperti itu adalah solusi terbaik, karena + = membuat objek baru setiap loop rutin dijalankan.
perilbrain
3
apakah Anda tahu bahwa toString () nya tidak dipanggil dalam satu lingkaran?
Omry Yadan
Saya sudah mencoba contoh ini untuk menguji kecepatan. Jadi hasil saya adalah: lambat berlalu 29.672 ms; cepat berlalu 15 ms. Jadi jawabannya sudah jelas. Tetapi jika itu akan menjadi 100 iterasi - waktu adalah sama - 0 ms. Jika 500 iterasi - 16 ms dan 0 ms. Dan seterusnya.
Ernestas Gruodis
28

Saya juga telah berbenturan dengan bos saya pada kenyataan apakah akan menggunakan append atau +. Ketika mereka menggunakan Append (saya masih tidak bisa mencari tahu seperti yang mereka katakan setiap kali sebuah objek baru dibuat). Jadi saya berpikir untuk melakukan beberapa penelitian dan pengembangan. Meskipun saya menyukai 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 yang dibuat.

perilbrain
sumber
27

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

Jadi demi pembacaan kode, gunakan "+".

2 pengecualian:

  • lingkungan multithreaded: StringBuffer
  • rangkaian dalam loop: StringBuilder / StringBuffer
Zofren
sumber
22

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

Menambahkan string menggunakan + dalam for for loop

Jawa:

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

ByteCode :( forloop excerpt)

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 :( forloop excerpt)

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 . Dalam kasus pertama, di mana +digunakan, baru StringBuilderdibuat 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 tidak Anda perlukan saat menggunakan +operator dalam forloop.

Pembawa cincin
sumber
1
Apakah ini Oracle JDK atau OpenJDK?
Christophe Roussy
12

Di Java 9 versi 1 harus lebih cepat karena dikonversi menjadi invokedynamicpanggilan. Rincian lebih lanjut dapat ditemukan di JEP-280 :

Idenya adalah untuk mengganti seluruh tarian append StringBuilder dengan panggilan invokedynamic sederhana ke java.lang.invoke.StringConcatFactory, yang akan menerima nilai-nilai yang membutuhkan penggabungan.

ZhekaKozlov
sumber
9

Untuk alasan kinerja, penggunaan +=( Stringpenggabungan) tidak disarankan. Alasannya adalah: Java Stringtidak berubah, setiap kali penggabungan baru dilakukan, baru Stringdibuat (yang baru memiliki sidik jari yang berbeda dari yang lebih lama yang sudah ada di kumpulan String ). Membuat string baru memberi tekanan pada GC dan memperlambat program: pembuatan objek itu mahal.

Kode di bawah ini harus membuatnya lebih praktis dan jelas pada saat bersamaan.

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);
}

Hasil untuk lari dilaporkan di bawah ini.

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

Tidak mempertimbangkan hasil untuk 1 concatenation (JIT belum melakukan tugasnya), bahkan untuk 10 concatenation penalti kinerja relevan; untuk ribuan penggabungan, perbedaannya sangat besar.

Pelajaran yang dapat dipetik dari percobaan yang sangat cepat ini (mudah direproduksi dengan kode di atas): jangan pernah gunakan +=untuk menyatukan senar, bahkan dalam kasus yang sangat mendasar di mana beberapa penggabungan diperlukan (seperti dikatakan, membuat string baru tetap mahal dan tetap menekan pada GC).

Paolo Maresca
sumber
9

Lihat contoh di bawah ini:

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

Keluaran:

versi bytecode java: 8
java.version: 1.8.0_144
[str1.concat (str2)]
rata-rata waktu untuk 10.000 concatenations: rata-
rata waktu rata- rata untuk 10.000 concatenations: 0,096 ms:
waktu rata- rata 0,185 ms rata- rata untuk 10000 concatenations: rata-
rata waktu rata- rata avg untuk ms: Rangkaian 10000: rata-
rata rata- rata 0,501 ms untuk Rangkaian 10000: rata- rata 0,656 ms,
Diciptakan untai panjang: 1950000 dalam 17745 ms
[str1 + = str2]
rata-rata waktu untuk 10.000 rangkai: rata-
rata waktu rata- rata untuk 0,00 ms : untuk rata- rata 10000: 0,252 rata-
rata pada rata- rata waktu untuk Rangkaian 10000:
Rata-rata waktu 1,129 ms rata- rata untuk Rangkaian 10000: 1,727 ms rata-rata
waktu rata-rata untuk 10.000 gabungan: 2.302 ms rata-rata.
Diciptakan string panjang: 1950000 dalam 60279 ms
[str1.append (str2)]
rata-rata waktu untuk 10.000 gabungan: 0.002 ms rata-
rata waktu rata- rata untuk 10.000 gabungan: 0.002 ms rata-
rata waktu rata- rata untuk 10.000 gabungan: 0,002 ms rata-
rata waktu rata- rata untuk 10000 concatenations: 0,002 ms rata-
rata rata- rata waktu untuk 10000 concatenations: 0,002 ms rata-rata
Diciptakan string panjang: 1950000 dalam 100 ms

Ketika panjang string meningkat, begitu juga waktu penggabungan.
Di situlah StringBuilderpasti diperlukan.
Seperti yang Anda lihat, gabungan:, UUID.randomUUID()+"---"tidak terlalu mempengaruhi waktu.

PS: Saya tidak berpikir Kapan menggunakan StringBuilder di Jawa adalah duplikat dari ini.
Pertanyaan ini berbicara tentang toString()yang sebagian besar kali tidak melakukan rangkaian string besar.


Pembaruan 2019

Sejak java8saat itu, banyak hal berubah sedikit. Tampaknya sekarang (java13), waktu penyatuan +=praktis sama dengan str.concat(). Namun StringBuilderwaktu penggabungan masih konstan . (Posting asli di atas sedikit diedit untuk menambah lebih banyak keluaran verbose)

versi bytecode java: 13
java.version: 13.0.1
[str1.concat (str2)]
waktu rata-rata untuk 10.000 concatenations: rata-
rata waktu rata- rata untuk 0,010 ms untuk concatenations: 0,1 ms rata-
rata waktu rata- rata untuk 10000 concatations: 0,17 ms rata-
rata waktu rata- rata untuk 10000 concatenations: rata-
rata 0.255 ms rata- rata waktu untuk 10000 concatenations: rata-rata 0.336 ms avg
Dibuat string panjang: 1950000 di 9147 ms
[str1 + = str2]
waktu rata-rata untuk 10.000 concatenations: rata-
rata waktu rata- rata 0.037 ms untuk concatenations: 0,07 ms rata-
rata waktu untuk avg Rangkaian 10000: rata-
rata 0.249 ms rata- rata untuk 10.000 Rangkaian: rata-rata 0.298 ms
waktu rata-rata untuk 10.000 gabungan: 0,326 ms rata-rata.
Diciptakan string panjang: 1950000 dalam 10191 ms
[str1.append (str2)]
rata-rata waktu untuk 10.000 gabungan: rata-
rata 0,001 ms rata- rata untuk 10.000 gabungan: 0,001 ms rata-
rata waktu rata- rata untuk 10.000 penggabungan:
Rata-rata waktu 0,001 ms rata-
rata untuk 10.000
konkatasi: Rata- rata rata- rata 0,001 ms rata- rata untuk 10.000: rata- rata 0,001 ms Diciptakan panjang string: 1950000 dalam 43 ms

Patut dicatat juga bytecode:8/java.version:13kombinasi memiliki manfaat kinerja yang baik dibandingkan denganbytecode:8/java.version:8

Marinos An
sumber
Ini harus menjadi jawaban yang diterima .. itu tergantung pada ukuran String Stream yang menentukan pilihan concat atau StringBuilder
user1428716
@ user1428716 FYI: jawaban diperbarui dengan hasil java13yang kini berbeda. Tapi saya pikir kesimpulan utamanya tetap sama.
Marinos An
7

Apache Commons-Lang memiliki kelas ToStringBuilder yang super mudah digunakan. Itu melakukan pekerjaan yang baik dari kedua penanganan append-logic serta memformat bagaimana Anda ingin toString Anda terlihat.

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

Akan mengembalikan output yang terlihat seperti com.blah.YourClass@abc1321f[a=whatever, b=foo].

Atau dalam bentuk yang lebih kental menggunakan chaining:

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

Atau jika Anda ingin menggunakan refleksi untuk memasukkan setiap bidang kelas:

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

Anda juga dapat menyesuaikan gaya ToString jika Anda mau.

matt b
sumber
4

Jadikan metode toString terbaca sebanyak mungkin!

Satu-satunya pengecualian untuk ini dalam buku saya adalah jika Anda dapat membuktikan kepada saya bahwa ia menghabiskan sumber daya yang signifikan :) (Ya, ini berarti membuat profil)

Perhatikan juga bahwa kompiler Java 5 menghasilkan kode lebih cepat daripada pendekatan "StringBuffer" tulisan tangan yang digunakan dalam versi Java yang lebih lama. Jika Anda menggunakan "+" ini, peningkatan di masa mendatang akan datang secara gratis.

Thorbjørn Ravn Andersen
sumber
3

Tampaknya ada beberapa perdebatan apakah menggunakan StringBuilder masih diperlukan dengan kompiler saat ini. Jadi saya pikir saya akan memberikan 2 sen pengalaman saya.

Saya memiliki JDBCset hasil 10k catatan (ya, saya membutuhkan semuanya dalam satu batch.) Menggunakan + operator memakan waktu sekitar 5 menit pada mesin saya dengan Java 1.8. Menggunakan stringBuilder.append("")membutuhkan waktu kurang dari satu detik untuk permintaan yang sama.

Jadi perbedaannya sangat besar. Di dalam lingkaran StringBuilderjauh lebih cepat.

Eddy
sumber
2
Saya pikir perdebatannya adalah tentang menggunakannya di luar lingkaran. Saya pikir ada konsensus yang Anda butuhkan untuk menggunakannya dalam satu lingkaran.
Jordan
2

Rangkaian string yang bijak berkinerja baik menggunakan '+' lebih mahal karena harus membuat salinan String yang sama sekali baru karena String tidak dapat diubah di java. Ini memainkan peran tertentu jika penggabungan sangat sering, misalnya: di dalam lingkaran. Berikut ini yang disarankan IDEA saya ketika saya berupaya melakukan hal seperti itu:

masukkan deskripsi gambar di sini

Aturan umum:

  • Dalam penugasan string tunggal, menggunakan penggabungan string baik-baik saja.
  • Jika Anda mengulang untuk membuat blok besar data karakter, pilih StringBuffer.
  • Menggunakan + = pada sebuah String akan selalu kurang efisien daripada menggunakan StringBuffer, jadi itu harus membunyikan lonceng peringatan - tetapi dalam kasus-kasus tertentu optimasi yang diperoleh akan diabaikan dibandingkan dengan masalah keterbacaan, jadi gunakan akal sehat Anda.

Berikut ini adalah blog Jon Skeet yang bagus untuk topik ini.

Sudip Bhandari
sumber
2
Anda tidak boleh menggunakan StringBuffer kecuali Anda benar-benar membutuhkan akses tersinkronisasi dari banyak utas. Kalau tidak, lebih suka StringBuilder yang tidak disinkronkan dan karenanya memiliki overhead yang lebih sedikit.
Erki der Loony
1

Dapatkah saya menunjukkan bahwa jika Anda akan beralih ke koleksi dan menggunakan StringBuilder, Anda mungkin ingin memeriksa Apache Commons Lang dan StringUtils.join () (dalam berbagai rasa)?

Terlepas dari kinerja, itu akan menghemat Anda harus membuat StringBuilders dan untuk loop untuk apa yang tampaknya seperti yang kesekian kalinya.

Brian Agnew
sumber
1

Inilah yang saya periksa di Java8

  • Menggunakan Rangkaian string
  • Menggunakan 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";
    }

Ini benar-benar mimpi buruk jika Anda menggunakan penggabungan string untuk sejumlah besar string.

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms
nagendra547
sumber
-1

Saya pikir kita harus pergi dengan pendekatan append StringBuilder. Alasannya adalah:

  1. String concatenate akan membuat objek string baru setiap kali (Karena String adalah objek yang tidak dapat diubah), maka ia akan membuat 3 objek.

  2. Dengan String builder, hanya satu objek yang akan dibuat [StringBuilder bisa berubah] dan string selanjutnya ditambahkan ke sana.

VishalTambe
sumber
Mengapa jawaban ini tidak dipilih? docs.oracle.com/javase/8/docs/api/java/util/stream/… - Mutable Reduction
user1428716
-4

Untuk string sederhana seperti itu saya lebih suka menggunakan

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

Agar, saya akan mengatakan metode yang lebih disukai untuk membangun sebuah string menggunakan StringBuilder, String # concat (), kemudian operator + yang kelebihan beban. StringBuilder adalah peningkatan kinerja yang signifikan ketika bekerja string besar seperti menggunakan operator + adalah penurunan besar dalam kinerja (penurunan secara eksponensial besar ketika ukuran String meningkat). Satu masalah dengan menggunakan .concat () adalah ia bisa melempar NullPointerExceptions.

Droo
sumber
9
Menggunakan concat () cenderung berkinerja lebih buruk daripada '+' karena JLS memungkinkan '+' untuk dikonversi ke StringBuilder, dan kemungkinan besar semua JVM melakukannya atau menggunakan alternatif yang lebih efisien - hal yang sama tidak mungkin terjadi pada concat yang harus membuat dan membuang setidaknya satu string perantara lengkap dalam contoh Anda.
Lawrence Dol