String, StringBuffer, dan StringBuilder

213

Tolong beritahu saya situasi real time untuk membandingkan String, StringBufferdan StringBuilder?

Pengguna Java
sumber

Jawaban:

378

Perbedaan Mutabilitas:

Stringadalah kekal , jika Anda mencoba untuk mengubah nilai-nilai mereka, objek lain akan dibuat, sedangkan StringBufferdan StringBuilderyang bisa berubah sehingga mereka dapat mengubah nilai-nilai mereka.

Perbedaan Thread-Safety:

Perbedaan antara StringBufferdan StringBuilderapakah itu StringBufferaman-thread. Jadi ketika aplikasi perlu dijalankan hanya dalam satu utas maka lebih baik digunakan StringBuilder. StringBuilderlebih efisien daripada StringBuffer.

Situasi:

  • Jika string Anda tidak akan berubah, gunakan kelas String karena Stringobjek tidak dapat diubah.
  • Jika string Anda dapat berubah (misalnya: banyak logika dan operasi dalam pembuatan string) dan hanya akan diakses dari satu utas, penggunaan a StringBuildercukup baik.
  • Jika string Anda dapat berubah, dan akan diakses dari banyak utas, gunakan StringBufferkarena StringBuffersinkron sehingga Anda memiliki keamanan utas.
bakkal
sumber
16
Juga, menggunakan String untuk operasi logika agak lambat, dan tidak disarankan sama sekali, karena JVM mengubah String ke StringBuffer dalam bytecode. Banyak overhead yang terbuang untuk mengkonversi dari String ke StringBuffer dan kemudian kembali ke String lagi.
Pieter van Niekerk
2
Jadi Stringsketika kita mengubah nilai objek lain dibuat. Apakah referensi objek lama dibatalkan sehingga mungkin sampah yang dikumpulkan oleh GCatau bahkan sampah yang dikumpulkan?
Harsh Vardhan
@ PetietervanNiekerk Apa yang Anda maksud dengan operasi logika?
emlai
operasi logika yang saya maksud adalah yang String dasar, Sekarang satu hal yang ingin saya tanyakan, seperti yang dinyatakan oleh @Peter haruskah kita mulai menggunakan StringBuffer sebagai gantinya pada String dalam semua kasus atau ada beberapa kasus tertentu?
JavaDragon
@bakkal Bisakah saya menggunakan semua metode Stringdengan StringBuilder?
roottraveller
47
  • Anda menggunakannya Stringsaat struktur yang tidak dapat diubah sesuai; memperoleh urutan karakter baru dari a Stringdapat membawa penalti kinerja yang tidak dapat diterima, baik dalam waktu CPU atau memori (memperoleh substring adalah CPU yang efisien karena data tidak disalin, tetapi ini berarti jumlah data yang berpotensi jauh lebih besar mungkin tetap dialokasikan).
  • Anda menggunakan StringBuildersaat Anda perlu membuat urutan karakter yang bisa berubah, biasanya untuk menyatukan beberapa urutan karakter secara bersamaan.
  • Anda menggunakan StringBufferdalam keadaan yang sama Anda akan gunakan StringBuilder, tetapi ketika perubahan pada string yang mendasarinya harus disinkronkan (karena beberapa utas membaca / memodifikasi dalam buffer string).

Lihat contoh di sini .

Artefacto
sumber
2
singkat, tetapi tidak lengkap, ia kehilangan alasan mendasar untuk menggunakan StringBuilder / Buffer dan itu adalah untuk mengurangi atau menghilangkan alokasi ulang dan salinan array dari perilaku penggabungan String biasa.
1
"Anda menggunakan String ketika Anda berurusan dengan string yang tidak dapat diubah" - tidak masuk akal. Contoh String tidak dapat diubah, jadi mungkin komentar tersebut harus bertuliskan "Gunakan String ketika penggunaan memori karena ketidakberubahan tidak menjadi masalah". Jawaban yang diterima mencakup pangkalan itu dengan cukup baik.
fooMonster
28

Dasar:

Stringadalah kelas yang tidak dapat diubah, tidak dapat diubah. StringBuilderadalah kelas yang bisa berubah yang dapat ditambahkan ke, karakter diganti atau dihapus dan akhirnya dikonversi ke String StringBufferadalah versi asli disinkronkanStringBuilder

Anda harus memilih StringBuilderdalam semua kasus di mana Anda hanya memiliki satu utas mengakses objek Anda.

Rinciannya:

Juga perhatikan bahwa StringBuilder/Buffersitu bukan sihir, mereka hanya menggunakan Array sebagai objek pendukung dan bahwa Array harus dialokasikan kembali ketika sudah penuh. Pastikan dan buat StringBuilder/Bufferobjek Anda cukup besar di tempat asalnya tidak harus selalu diukur ulang setiap kali .append()dipanggil.

Pengubahan ukuran bisa sangat memburuk. Ini pada dasarnya kembali ukuran Array dukungan untuk 2 kali ukuran saat ini setiap kali perlu diperluas. Ini dapat menyebabkan sejumlah besar RAM mendapatkan alokasi dan tidak digunakan ketika StringBuilder/Bufferkelas mulai tumbuh besar.

Di Jawa String x = "A" + "B";menggunakan di StringBuilderbelakang layar. Jadi untuk kasus-kasus sederhana tidak ada manfaatnya menyatakan milik Anda sendiri. Tetapi jika Anda membangun Stringobjek yang besar, katakanlah kurang dari 4k, maka menyatakan StringBuilder sb = StringBuilder(4096);jauh lebih efisien daripada penggabungan atau menggunakan konstruktor default yang hanya 16 karakter. Jika Anda Stringakan kurang dari 10k maka inisialisasi dengan konstruktor ke 10k agar aman. Tetapi jika diinisialisasi ke 10k maka Anda menulis 1 karakter lebih dari 10k, itu akan dialokasikan kembali dan disalin ke array 20k. Jadi menginisialisasi tinggi lebih baik daripada rendah.

Dalam kasus ukuran ulang otomatis, pada karakter ke-17, Array dukungan akan dialokasikan kembali dan disalin ke 32 karakter, pada karakter ke-33 ini terjadi lagi dan Anda bisa mengalokasikan kembali dan menyalin Array ke dalam 64 karakter. Anda dapat melihat bagaimana ini merosot ke banyak alokasi ulang dan salinan yang merupakan apa yang Anda benar-benar coba hindari menggunakan StringBuilder/Bufferdi tempat pertama.

Ini dari kode sumber JDK 6 untuk AbstractStringBuilder

   void expandCapacity(int minimumCapacity) {
    int newCapacity = (value.length + 1) * 2;
        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
        newCapacity = minimumCapacity;
    }
        value = Arrays.copyOf(value, newCapacity);
    }

Praktik terbaik adalah menginisialisasi yang StringBuilder/Buffersedikit lebih besar dari yang Anda pikir akan Anda butuhkan jika Anda tidak tahu berapa besar Stringakan tetapi Anda bisa menebaknya. Satu alokasi memori yang sedikit lebih banyak daripada yang Anda butuhkan akan lebih baik daripada banyak alokasi ulang dan salinan.

Juga berhati-hatilah dalam menginisialisasi a StringBuilder/Bufferdengan Stringyang hanya akan mengalokasikan ukuran karakter String + 16, yang dalam kebanyakan kasus hanya akan memulai alokasi ulang yang merosot dan menyalin siklus yang Anda coba hindari. Berikut ini adalah langsung dari kode sumber Java 6.

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
    }

Jika Anda kebetulan berakhir dengan contoh StringBuilder/Bufferyang tidak Anda buat dan tidak dapat mengontrol konstruktor yang dipanggil, ada cara untuk menghindari perilaku alokasi ulang dan salin yang merosot. Panggil .ensureCapacity()dengan ukuran yang Anda inginkan untuk memastikan hasil Anda Stringsesuai.

Alternatif:

Sama seperti catatan, jika Anda melakukan pembangunan dan manipulasi yang sangat berat String , ada alternatif yang lebih berorientasi pada kinerja yang disebut Tali .

Alternatif lain, adalah membuat StringListimplementasi dengan subklasifikasi ArrayList<String>, dan menambahkan penghitung untuk melacak jumlah karakter pada setiap .append()dan operasi mutasi lainnya dari daftar, kemudian mengganti .toString()untuk membuat StringBuilderukuran persis yang Anda butuhkan dan mengulang-ulang daftar dan membangun output, Anda bahkan dapat membuat StringBuildervariabel instan dan 'cache' hasil .toString()dan hanya perlu menghasilkan kembali ketika sesuatu berubah.

Juga jangan lupa String.format()ketika membangun output terformat tetap, yang dapat dioptimalkan oleh kompiler karena mereka membuatnya lebih baik.

user177800
sumber
1
Apakah String x = "A" + "B";benar-benar mengkompilasi menjadi StringBuilder? Mengapa tidak dikompilasi saja String x = "AB";, seharusnya hanya menggunakan StringBuilder jika komponen tidak diketahui pada waktu kompilasi.
Matt Greer
mungkin mengoptimalkan konstanta String keluar, saya tidak dapat mengingat dari terakhir kali mendekompilasi kode byte, tetapi saya tahu bahwa jika ada variabel di sana akan menggunakan implementasi StringBuilder pasti. Anda dapat mengunduh kode sumber JDK dan mencari tahu sendiri. "A" + "B" adalah contoh yang dibuat-buat.
Saya bertanya-tanya tentang String.format (). Saya belum pernah benar-benar melihatnya digunakan dalam proyek. Biasanya itu adalah StringBuilder. Yah, biasanya itu sebenarnya "A" + "B" + "C" karena orang-orang malas;) Saya cenderung selalu menggunakan StringBuilder, bahkan jika itu hanya dua string yang digabungkan, karena di masa depan, mungkin lebih banyak string akan ditambahkan . Saya tidak pernah menggunakan String.format () terutama karena saya tidak pernah ingat apa itu JDK yang diperkenalkan - saya lihat itu JDK1.5, saya akan menggunakannya untuk opsi-opsi lain.
jamiebarrow
9

Apakah maksud Anda, untuk penggabungan?

Contoh dunia nyata: Anda ingin membuat string baru dari banyak lainnya .

Misalnya untuk mengirim pesan:

Tali

String s = "Dear " + user.name + "<br>" + 
" I saw your profile and got interested in you.<br>" +
" I'm  " + user.age + "yrs. old too"

StringBuilder

String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" ) 
          .append(" I saw your profile and got interested in you.<br>") 
          .append(" I'm  " ).append( user.age ).append( "yrs. old too")
          .toString()

Atau

String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as  fuzzy lollipop points out.

StringBuffer (sintaksnya persis seperti dengan StringBuilder, efeknya berbeda)

Tentang

StringBuffer vs. StringBuilder

Yang pertama disinkronkan dan kemudian tidak.

Jadi, jika Anda menjalankannya beberapa kali dalam satu utas (yang merupakan 90% dari semua casing), StringBuilderakan berjalan jauh lebih cepat karena tidak akan berhenti untuk melihat apakah ia memiliki kunci utas.

Jadi, disarankan untuk menggunakan StringBuilder(kecuali tentu saja Anda memiliki lebih dari satu utas mengaksesnya pada saat yang sama, yang jarang terjadi)

StringRangkaian ( menggunakan operator + ) dapat dioptimalkan oleh kompiler untuk digunakan di StringBuilderbawahnya, jadi, itu bukan lagi sesuatu yang perlu dikhawatirkan, di masa tua Jawa, ini adalah sesuatu yang semua orang katakan harus dihindari dengan cara apa pun, karena setiap rangkaian membuat objek String baru. Kompiler modern tidak melakukan ini lagi, tetapi tetap merupakan praktik yang baik untuk digunakan StringBuildersebagai gantinya jika Anda menggunakan kompiler "lama".

sunting

Hanya untuk yang penasaran, inilah yang dilakukan oleh kompiler untuk kelas ini:

class StringConcatenation {
    int x;
    String literal = "Value is" + x;
    String builder = new StringBuilder().append("Value is").append(x).toString();
}

javap -c StringConcatenation

Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;

java.lang.String literal;

java.lang.String builder;

StringConcatenation();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/StringBuilder
   8:   dup
   9:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   12:  ldc #4; //String Value is
   14:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_0
   18:  getfield    #6; //Field x:I
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   24:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27:  putfield    #9; //Field literal:Ljava/lang/String;
   30:  aload_0
   31:  new #2; //class java/lang/StringBuilder
   34:  dup
   35:  invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   38:  ldc #4; //String Value is
   40:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   43:  aload_0
   44:  getfield    #6; //Field x:I
   47:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   50:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   53:  putfield    #10; //Field builder:Ljava/lang/String;
   56:  return

}

Baris bernomor 5 - 27 adalah untuk String yang bernama "literal"

Baris bernomor 31-53 untuk String bernama "builder"

Tidak ada perbedaan, kode yang sama persis dijalankan untuk kedua string.

OscarRyz
sumber
2
ini adalah contoh yang sangat buruk. Menginisialisasi StringBuilder dengan "Dear" berarti .append () pertama akan menyebabkan realokasi dan menyalin. Sepenuhnya meniadakan efisiensi apa pun yang Anda coba dapatkan dari gabungan "normal". Contoh yang lebih baik adalah dengan membuatnya dengan ukuran awal yang akan menampung seluruh isi String akhir.
1
Biasanya BUKAN merupakan praktik yang baik untuk menggunakan StringBuildergabungan string to do di sisi kanan penugasan. Setiap implementasi yang baik akan menggunakan StringBuilder di belakang layar seperti yang Anda katakan. Selanjutnya, contoh Anda "a" + "b"akan dikompilasi menjadi satu literal "ab"tetapi jika Anda menggunakannya StringBuilderakan menghasilkan dua panggilan yang tidak perlu append().
Mark Peters
@ Mark saya tidak bermaksud menggunakan "a"+"b"tetapi untuk mengatakan apa yang merupakan rangkaian String tentang saya mengubahnya menjadi eksplisit. Apa yang tidak Anda katakan adalah, mengapa bukan praktik yang baik untuk melakukannya. Itulah yang dilakukan oleh kompiler (modern). @ fuzzy, saya setuju, khususnya ketika Anda tahu ukuran string terakhir (aprox).
OscarRyz
1
Saya tidak berpikir itu praktik yang sangat buruk , tetapi saya tentu tidak ingin rekomendasi untuk melakukan itu secara umum. Kikuk dan sulit dibaca, dan terlalu bertele-tele. Plus, itu mendorong kesalahan seperti milik Anda di mana Anda memecah dua literal yang dapat dikompilasi menjadi satu. Hanya jika profiling memberi tahu saya itu akan membuat perbedaan, saya akan melakukannya dengan cara Anda.
Mark Peters
@Mark Mengerti. Saya berpikir lebih dalam "sebagian besar kode seperti template" dan tidak benar-benar di setiap string literal biasa. Tapi ya, saya setuju, karena mereka melakukan hal yang sama saat ini, itu tidak masuk akal (10 tahun yang lalu adalah alasan untuk menolak perubahan dalam revisi kode) :)
OscarRyz
8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
Area Penyimpanan | Constant String Pool Heap Heap
Dapat dimodifikasi | Tidak (tidak berubah) Ya (bisa berubah) Ya (bisa berubah)
Thread Aman | Ya, Tidak
 Performa | Cepat Sangat lambat Cepat
-------------------------------------------------- --------------------------------
Abhijit Maity
sumber
Mengapa kinerja String cepat dan kinerja StringBuffer sangat lambat?
gaurav
1
@gaurav Anda dapat membaca kode sumbernya, semua metode di StringBuffer adalah synchroniseddan itulah sebabnya .
Dengar
8

Keluarga Tali

Tali

The String classmewakili karakter string. Semua string literal dalam program Java, seperti "abc"diimplementasikan sebagai instance dari kelas ini.

Objek string tidak dapat diubah setelah dibuat, kita tidak dapat mengubah. ( String adalah konstanta )

  • Jika sebuah String dibuat menggunakan konstruktor atau metode maka string tersebut akan disimpan dalam Heap Memory juga SringConstantPool. Tetapi sebelum menyimpan di kolam itu memanggil intern()metode untuk memeriksa ketersediaan objek dengan konten yang sama di kolam menggunakan metode sama. Jika String-copy tersedia di Pool maka mengembalikan referensi. Jika tidak, objek String ditambahkan ke kumpulan dan mengembalikan referensi.

    • Bahasa Java menyediakan dukungan khusus untuk operator rangkaian string ( +), dan untuk konversi objek lain ke string. Rangkaian string diimplementasikan melalui kelas StringBuilder (atau StringBuffer) dan metode appendnya.

    String heapSCP = new String("Yash");
    heapSCP.concat(".");
    heapSCP = heapSCP + "M";
    heapSCP = heapSCP + 777;
    
    // For Example: String Source Code 
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
    
  • String literal disimpan di StringConstantPool.

    String onlyPool = "Yash";

StringBuilder dan StringBuffer adalah urutan karakter yang bisa berubah. Itu berarti seseorang dapat mengubah nilai objek ini. StringBuffer memiliki metode yang sama dengan StringBuilder, tetapi setiap metode di StringBuffer disinkronkan sehingga aman untuk thread.

  • Data StringBuffer dan StringBuilder hanya dapat dibuat menggunakan operator baru. Jadi, mereka disimpan dalam memori Heap.

  • Contoh dari StringBuilder tidak aman untuk digunakan oleh banyak utas. Jika sinkronisasi seperti itu diperlukan maka disarankan agar StringBuffer digunakan.

    StringBuffer threadSafe = new StringBuffer("Yash");
    threadSafe.append(".M");
    threadSafe.toString();
    
    StringBuilder nonSync = new StringBuilder("Yash");
    nonSync.append(".M");
    nonSync.toString();
    
  • StringBuffer dan StringBuilder memiliki metode khusus seperti., replace(int start, int end, String str)Dan reverse().

    CATATAN : StringBuffer dan SringBuilder bisa berubah karena mereka menyediakan implementasi Appendable Interface.


Kapan harus menggunakan yang mana.

  • Jika a Anda tidak akan mengubah nilai setiap kali maka lebih baik Digunakan String Class. Sebagai bagian dari Generik jika Anda ingin Menyortir Comparable<T>atau membandingkan nilai, lalu gunakan String Class.

    //ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
    Set<StringBuffer> set = new TreeSet<StringBuffer>();
    set.add( threadSafe );
    System.out.println("Set : "+ set);
  • Jika Anda akan mengubah nilai setiap kali pergi untuk StringBuilder yang lebih cepat dari StringBuffer. Jika beberapa utas mengubah nilai, gunakan StringBuffer.

Yash
sumber
4

Juga, StringBufferaman-thread, yang StringBuildertidak.

Jadi dalam situasi waktu nyata ketika utas yang berbeda mengaksesnya, StringBuilderdapat memiliki hasil yang tidak ditentukan.

Lars Andren
sumber
3

Catatan bahwa jika Anda menggunakan Java 5 atau yang lebih baru, Anda harus menggunakan StringBuilderbukan StringBuffer. Dari dokumentasi API:

Pada rilis JDK 5, kelas ini telah dilengkapi dengan kelas setara yang dirancang untuk digunakan oleh satu utas StringBuilder,. The StringBuilderkelas umumnya harus digunakan dalam preferensi untuk yang satu ini, karena mendukung semua operasi yang sama tetapi lebih cepat, karena melakukan tidak ada sinkronisasi.

Dalam praktiknya, Anda hampir tidak akan pernah menggunakan ini dari banyak utas secara bersamaan, sehingga sinkronisasi yang StringBufferdilakukannya hampir selalu tidak perlu di atas kepala.

Jesper
sumber
3

Secara pribadi, saya tidak berpikir ada gunanya menggunakan dunia nyata StringBuffer. Kapan saya ingin berkomunikasi antara banyak utas dengan memanipulasi urutan karakter? Kedengarannya tidak berguna sama sekali, tapi mungkin saya belum melihat cahaya :)

fredoverflow
sumber
3

Perbedaan antara String dan dua kelas lainnya adalah bahwa String tidak dapat diubah dan dua lainnya adalah kelas yang bisa berubah.

Tetapi mengapa kita memiliki dua kelas untuk tujuan yang sama?

Alasannya adalah StringBufferThread aman dan StringBuildertidak. StringBuilderadalah kelas baru StringBuffer Apidan diperkenalkan JDK5dan selalu disarankan jika Anda bekerja di lingkungan berulir tunggal seperti yang banyakFaster

Untuk Perincian Lengkap, Anda dapat membaca http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

Hitesh Garg
sumber
2

Di java, String tidak dapat diubah. Menjadi kekal kita maksudkan bahwa sekali String dibuat, kita tidak bisa mengubah nilainya. StringBuffer bisa berubah. Setelah objek StringBuffer dibuat, kami hanya menambahkan konten ke nilai objek alih-alih membuat objek baru. StringBuilder mirip dengan StringBuffer tetapi tidak aman untuk thread. Metode StingBuilder tidak disinkronkan tetapi dibandingkan dengan String lainnya, Stringbuilder berjalan tercepat. Anda dapat mempelajari perbedaan antara String, StringBuilder dan StringBuffer dengan mengimplementasikannya.

rajat ghai
sumber