Menghapus buffer / pembangun string setelah loop

122

Bagaimana Anda menghapus buffer string di Java setelah loop sehingga iterasi berikutnya menggunakan buffer string yang jelas?

air terjun
sumber
2
Pertanyaan lain: mengapa Anda menggunakan SB hanya untuk satu iterasi loop? Apakah ada iterasi batin lainnya? SB tidak layak jika Anda hanya ingin melakukan A + B + C + D (kompiler Java secara internal akan menggunakan SB). Ini membantu jika Anda ingin menambahkan bagian-bagian string secara bersyarat, tetapi sebaliknya ... cukup gunakan "+".
helios

Jawaban:

135

Salah satu opsinya adalah menggunakan metode delete sebagai berikut:

StringBuffer sb = new StringBuffer();
for (int n = 0; n < 10; n++) {
   sb.append("a");

   // This will clear the buffer
   sb.delete(0, sb.length());
}

Opsi lain (pembersih bit) menggunakan setLength (int len) :

sb.setLength(0);

Lihat Javadoc untuk info lebih lanjut:

Jon
sumber
15
Sesuatu yang sedikit lebih baik adalah dengan mendeklarasikan StringBuffer di dalam loop.
Mark Elliot
11
Ah, saya pikir sb.setLength (0); lebih bersih dan lebih efisien daripada mendeklarasikannya di dalam loop. Solusi Anda bertentangan dengan manfaat kinerja menggunakan StringBuffer ...
Jon
6
Saya pikir manfaat kinerja berasal dari mutabilitas string, bukan dari menyimpan instantiation. berikut adalah tes cepat dari 1e8 iterasi: loop dalam (2,97s): ideone.com/uyyTL14w , loop luar (2,87s): ideone.com/F9lgsIxh
Mark Elliot
6
Berbicara tentang kinerja: Kecuali kode Anda diakses dalam skenario multi-utas, Anda harus menggunakan StringBuilder daripada StringBuffer - lihat javadoc: "Jika memungkinkan, disarankan agar kelas ini digunakan dalam preferensi untuk StringBuffer karena akan lebih cepat di bawah sebagian besar implementasi ".
Rahel Lüthy
4
Satu-satunya keuntungan dari menciptakan SB di luar tidak kehilangan karakter internal (berpotensi panjang) darinya. Jika di iterator pertama sudah cukup besar, loop kedua tidak perlu mengubah ukuran char []. Tetapi untuk mendapatkan keuntungan, "metode yang jelas" harus mempertahankan ukuran array internal. setLength melakukan itu tetapi juga menyetel ke \ u0000 semua karakter yang tidak digunakan di SB, jadi performanya kurang dari sekadar membuat SB baru dengan kapasitas awal yang baik. Mendeklarasikan di dalam loop lebih baik.
helios
48

Cara termudah untuk menggunakan kembali StringBufferadalah dengan menggunakan metode inisetLength()

public void setLength(int newLength)

Anda mungkin memiliki kasus seperti ini

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb
Mossaddeque Mahmood
sumber
2
@MMahmoud, Dalam contoh kode itu harus dibaca, setLengthbukan setlength.
OnaBai
Ketika saya melihat sumber kode setLength, bagaimana cara kerjanya ( gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1 ) di sini jumlah akan selalu lebih besar daripada newLength (0)?
Thomas Decaux
20

Anda memiliki dua pilihan:

Salah satu penggunaan:

sb.setLength(0);  // It will just discard the previous data, which will be garbage collected later.  

Atau gunakan:

sb.delete(0, sb.length());  // A bit slower as it is used to delete sub sequence.  

CATATAN

Hindari mendeklarasikan StringBufferatau StringBuilderobjek dalam loop lain itu akan membuat objek baru dengan setiap iterasi. Membuat objek membutuhkan sumber daya sistem, ruang dan juga membutuhkan waktu. Jadi untuk jangka panjang, hindari mendeklarasikannya dalam loop jika memungkinkan.

Aditya Singh
sumber
5
buf.delete(0,  buf.length());
Suraj Chandran
sumber
5

Saya sarankan membuat yang baru StringBuffer(atau bahkan lebih baik, StringBuilder) untuk setiap iterasi. Perbedaan kinerja benar-benar dapat diabaikan, tetapi kode Anda akan lebih pendek dan sederhana.

Eli Acherkan
sumber
2
Jika Anda memiliki bukti perbedaan kinerja tersebut, silakan bagikan. (Saya juga akan senang melihat statistik di mana Java sebagian besar digunakan, dan di mana definisi "sebagian besar".)
Eli Acherkan
1
Telah diketahui bahwa alokasi (kata kunci baru di Java) lebih mahal daripada hanya mengubah beberapa bidang dari objek yang sama. Untuk "sebagian besar", yang sangat dapat Anda ganti, ada lebih dari 1,5 Miliar Perangkat Android, semuanya menggunakan Java.
Louis CAD
1
Saya setuju bahwa dalam beberapa kasus, keterbacaan kode lebih penting daripada kinerja, tetapi dalam kasus ini, itu hanya satu baris kode! Dan Anda dapat menjalankan benchmark yang akan membuktikan bahwa alokasi dan pembuatan objek, ditambah pengumpulan sampah lebih mahal daripada tidak melakukannya sama sekali. Karena kita berada dalam satu lingkaran, itu lebih dari relevan.
Louis CAD
1
Saya juga menganut pandangan Knuth, tetapi sudut pandangnya tidak selalu benar. Menambahkan hanya satu baris untuk mendapatkan siklus CPU dan memori sama sekali tidak jahat. Anda mengambil ide terlalu lurus. Perhatikan bahwa loop biasanya diulang berkali-kali. Sebuah loop dapat diulang ribuan kali, yang dapat memakan megabyte yang berharga di ponsel (dan juga di server atau komputer) jika tidak dioptimalkan dengan hati-hati.
Louis CAD
1
Saya pikir kami berdua menyatakan sudut pandang kami dengan cukup jelas, dan saya merasa bahwa diskusi lebih lanjut tidak akan bermanfaat untuk bagian komentar ini. Jika Anda merasa jawaban saya tidak benar, menyesatkan, atau tidak lengkap, Anda - atau siapa pun - boleh mengedit dan / atau menolaknya.
Eli Acherkan
5
public void clear(StringBuilder s) {
    s.setLength(0);
}

Pemakaian:

StringBuilder v = new StringBuilder();
clear(v);

untuk keterbacaan, saya rasa ini adalah solusi terbaik.

Ido Kessler
sumber
2

Jawaban udah bagus disana. Cukup tambahkan hasil benchmark untuk perbedaan kinerja StringBuffer dan StringBuild gunakan contoh baru dalam loop atau gunakan setLength (0) dalam loop.

Ringkasannya adalah: Dalam lingkaran besar

  • StringBuilder jauh lebih cepat daripada StringBuffer
  • Buat instance StringBuilder baru dalam loop tidak ada perbedaan dengan setLength (0). (setLength (0) memiliki keuntungan yang sangat sangat kecil daripada membuat instance baru.)
  • StringBuffer lebih lambat dari StringBuilder dengan membuat instance baru secara berulang
  • setLength (0) dari StringBuffer sangat lambat daripada membuat instance baru dalam perulangan.

Tolok ukur yang sangat sederhana (Saya baru saja mengubah kode secara manual dan melakukan pengujian yang berbeda):

public class StringBuilderSpeed {
public static final char ch[] = new char[]{'a','b','c','d','e','f','g','h','i'};

public static void main(String a[]){
    int loopTime = 99999999;
    long startTime = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for(int i = 0 ; i < loopTime; i++){
        for(char c : ch){
            sb.append(c);
        }
        sb.setLength(0);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time cost: " + (endTime - startTime));
}

}

Instance StringBuilder baru dalam loop: Biaya waktu: 3693, 3862, 3624, 3742

StringBuilder setLength: Biaya waktu: 3465, 3421, 3557, 3408

Instance StringBuffer baru dalam loop: Biaya waktu: 8327, 8324, 8284

StringBuffer setLength Waktu biaya: 22878, 23017, 22894

Sekali lagi StringBuilder setLength untuk memastikan labtop saya tidak mendapat masalah untuk digunakan selama itu untuk StringBuffer setLength :-) Biaya waktu: 3448

Hao
sumber
0
StringBuffer sb = new SringBuffer();
// do something wiht it
sb = new StringBuffer();

saya pikir kode ini lebih cepat.

Binh Phung
sumber
3
Ini akan memiliki masalah kinerja. Ini membuat objek baru setelah setiap iterasi
Aditya Singh
@AdityaSingh Ini adalah pendekatan yang masuk akal. Jangan menganggap masalah kinerja tanpa bukti.
shmosel
Mengasumsikan hal-hal berdasarkan pemahaman tentang apa yang sedang terjadi adalah dasar dari kecerdasan.
rghome