Hapus karakter terakhir dari StringBuilder?


423

Ketika Anda harus mengulang koleksi dan membuat string dari setiap data yang dipisahkan oleh pembatas, Anda selalu berakhir dengan pembatas tambahan di akhir, misalnya

for (String serverId : serverIds) {
  sb.append(serverId);
   sb.append(",");
}

Memberikan sesuatu seperti: serverId_1, serverId_2, serverId_3,

Saya ingin menghapus karakter terakhir di StringBuilder (tanpa mengubahnya karena saya masih membutuhkannya setelah loop ini).


11
Jika dengan menggabungkan string yang Anda maksud dengan "string concatenation", itu tergantung pada jumlah string dan panjangnya. Menggunakan string builder lebih efisien jika Anda akan memalu banyak string terlepas dari ukurannya karena string tidak dapat diubah. Setiap kali Anda menggabungkan string bersama, Anda membuat string hasil baru (yang benar-benar array char). Pembuat string pada dasarnya adalah daftar char yang tidak menjadi string yang tidak dapat diubah sampai Anda memanggil metode toString ().
dyslexicanaboko

4
Jika Anda menggunakan Java 8, cukup gunakan StringJoiner: stackoverflow.com/a/29169233/901641
ArtOfWarfare

Jawaban:


640

Orang lain telah menunjukkan deleteCharAtmetode ini, tetapi inilah pendekatan alternatif lain:

String prefix = "";
for (String serverId : serverIds) {
  sb.append(prefix);
  prefix = ",";
  sb.append(serverId);
}

Atau, gunakan Joinerkelas dari Jambu :)

Pada Java 8, StringJoineradalah bagian dari JRE standar.


7
@Coronatus: Tidak, karena "" tidak ada karakter apa pun, bukan karakter tunggal.
Jon Skeet

31
tidak akan menjalankan awalan = ","; setiap siklus mempengaruhi kinerja?
Harish

21
@ Harish: Mungkin, sedikit, sangat kecil - sangat kecil kemungkinannya untuk menjadi signifikan.
Jon Skeet

4
@ Harish - dan mungkin tidak sama sekali, jika optimizer membuka gulungan pengulangan pertama.
Stephen C

6
Apache Commons memang memiliki alternatif lain untuk jambu biji Joinerjuga di mereka StringUtils. commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… , java.lang.String)
GoRoS

419

Solusi sederhana lainnya adalah:

sb.setLength(sb.length() - 1);

Solusi yang lebih rumit:

Solusi di atas mengasumsikan bahwa sb.length() > 0... yaitu ada "karakter terakhir" untuk dihapus. Jika Anda tidak bisa membuat asumsi itu, dan / atau Anda tidak bisa berurusan dengan pengecualian yang akan terjadi jika asumsi itu tidak benar, maka periksa panjang StringBuilder terlebih dahulu; misalnya

// Readable version
if (sb.length() > 0) {
   sb.setLength(sb.length() - 1);
}

atau

// Concise but harder-to-read version of the above.
sb.setLength(Math.max(sb.length() - 1, 0));

23
Solusi yang sangat bagus Dampak terendah pada kinerja dan kode paling tidak diperlukan :)
Alain O'Dea

186
if(sb.length() > 0){
    sb.deleteCharAt(sb.length() - 1);
}

33
Ini terlalu banyak dipilih tetapi tidak efisien, ia melakukan system.arraycopy. Apa yang dikatakan @Rohit Reddy Korrapolu.
alianos-

13
Itu tidak aman untuk sb.length() == 0juga
Matthias

Apakah ini aman dengan karakter pasangan pengganti bermain?
rogerdpack

Dengan asumsi bahwa karakter terakhir adalah pemisah koma (seperti contoh), maka pengganti tidak ada bedanya. Jika Anda perlu menggeneralisasi maka kurangi separator.length()bukan 1.
Stephen C

61

Pada Java 8, kelas String memiliki metode statis join. Argumen pertama adalah string yang Anda inginkan di antara setiap pasangan string, dan argumen kedua adalah Iterable<CharSequence>(yang keduanya merupakan antarmuka, jadi sesuatu seperti List<String>berfungsi. Jadi, Anda bisa melakukan ini:

String.join(",", serverIds);

Juga di Java 8, Anda bisa menggunakan StringJoinerkelas baru , untuk skenario di mana Anda ingin mulai membangun string sebelum Anda memiliki daftar lengkap elemen untuk dimasukkan ke dalamnya.


nvm diedit untuk Anda jika Anda tidak keberatan, hapus komentar saya juga
Eugene

@Eugene - Saya menulis ulang jawaban sepenuhnya untuk fokus, String.joinbukan StringJoiner.
ArtOfWarfare

37

Dapatkan saja posisi kemunculan karakter terakhir.

for(String serverId : serverIds) {
 sb.append(serverId);
 sb.append(",");
}
sb.deleteCharAt(sb.lastIndexOf(","));

Karena lastIndexOfakan melakukan pencarian terbalik, dan Anda tahu itu akan ditemukan pada percobaan pertama, kinerja tidak akan menjadi masalah di sini.

EDIT

Karena saya terus mendapatkan jawaban saya (terima kasih kawan 😊), perlu diperhatikan bahwa:

Di Java 8 dan seterusnya hanya akan lebih mudah dibaca dan eksplisit untuk menggunakan StringJoiner . Ini memiliki satu metode untuk pemisah sederhana, dan kelebihan untuk awalan dan akhiran.

Contoh diambil dari sini: contoh

Contoh menggunakan pemisah sederhana:

    StringJoiner mystring = new StringJoiner("-");    

    // Joining multiple strings by using add() method  
    mystring.add("Logan");  
    mystring.add("Magneto");  
    mystring.add("Rogue");  
    mystring.add("Storm");  

    System.out.println(mystring);

Keluaran:

Logan-Magneto-Rogue-Storm

Contoh dengan akhiran dan awalan:

    StringJoiner mystring = new StringJoiner(",", "(", ")");    

    // Joining multiple strings by using add() method  
    mystring.add("Negan");  
    mystring.add("Rick");  
    mystring.add("Maggie");  
    mystring.add("Daryl");  

    System.out.println(mystring);

Keluaran

(Negan, Rick, Maggie, Daryl)


Anda akan yakin bahwa karakter terakhir adalah ,karena itu adalah pernyataan terakhir dari for loop. Ini lastInfexOflebih untuk keterbacaan dan menjadikannya no-brainer jika Anda tidak ingin mengingat apakah itu 0-diindeks atau tidak. Selanjutnya, Anda tidak perlu ikut campur dengan panjang stringbuilder. Hanya untuk kenyamanan.
Reuel Ribeiro

34

Pada kasus ini,

sb.setLength(sb.length() - 1);

lebih disukai karena hanya menetapkan nilai terakhir ke '\0'sedangkan menghapus karakter terakhir tidakSystem.arraycopy


1
The setLengthpanggilan tidak menetapkan apa-apa untuk nilai terakhir. Buffer string Java tidak dibatalkan nol / nol. Bahkan, setLengthhanya memperbarui lengthbidang.
Stephen C

@Rohit Reddy Korrapolu: Tapi arraycopyelemen copy 0, jadi saya kira itu bisa dioptimalkan jauh.
maaartinus

2
Jika argumen newLength lebih besar dari atau sama dengan panjang saat ini, karakter null yang cukup ('\ u0000') ditambahkan sehingga panjang menjadi argumen newLength. Yang tidak demikian.
fglez


11

Alternatif lain

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}
sb.deleteCharAt(0);

2
Seharusnya lebih baik daripada menghapus char terakhir karena ini membutuhkan perhitungan ukuran. Kecuali menghapus char pertama menyebabkan data dipindahkan ...
slott

8

Kalau tidak,

StringBuilder result = new StringBuilder();
for(String string : collection) {
    result.append(string);
    result.append(',');
}
return result.substring(0, result.length() - 1) ;

Dapat digunakan karena Anda dapat menambahkan "." pada akhirnya.
artfullyContrived

6
StringBuilder sb = new StringBuilder();
sb.append("abcdef");
sb.deleteCharAt(sb.length() - 1);
assertEquals("abcde",sb.toString());
// true

5

Alternatif lain:

public String join(Collection<String> collection, String seperator) {
    if (collection.isEmpty()) return "";

    Iterator<String> iter = collection.iterator();
    StringBuilder sb = new StringBuilder(iter.next());
    while (iter.hasNext()) {
        sb.append(seperator);
        sb.append(iter.next());
    }

    return sb.toString();
}

3

Untuk menghindari pengulangan (memengaruhi kinerja) prefixpenggunaan TextUtils.isEmpty:

            String prefix = "";
            for (String item : list) {
                sb.append(prefix);
                if (TextUtils.isEmpty(prefix))
                    prefix = ",";
                sb.append(item);
            }

Jenis paket apa yang dimiliki TestUtils?
Markus

@Markus android.text.TextUtils
NickUnuchek

1

Anda dapat mencoba menggunakan kelas 'Joiner' alih-alih menghapus karakter terakhir dari teks yang Anda buat;

                List<String> textList = new ArrayList<>();
                textList.add("text1");
                textList.add("text2");
                textList.add("text3");

                Joiner joiner = Joiner.on(",").useForNull("null");
                String output = joiner.join(textList);

               //output : "text1,text2,text3"

1

Saya melakukan sesuatu seperti di bawah ini:

    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < value.length; i++) {
        stringBuilder.append(values[i]);
        if (value.length-1) {
            stringBuilder.append(", ");
        }
    }

0

Ini solusi lain:

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}

String resultingString = "";
if ( sb.length() > 1 ) {
    resultingString = sb.substring(1);
}

1
Oh begitu. Anda memanggil substring pada StringBuilder bukan String.
Stephen C

Tapi bagaimanapun, ini hanya varian kecil pada solusi Zaki dari 2010.
Stephen C

0

Saya pribadi ingin menambahkan karakter backspace (atau lebih "pembatas" lebih lama) pada akhirnya:

for(String serverId : serverIds) {
    sb.append(serverId);
    sb.append(",");
}

sb.append('\b');

Perhatikan bahwa ada masalah:

  • bagaimana \bditampilkan tergantung pada lingkungan,
  • yang length()dari Stringkonten mungkin berbeda dari panjang "visibile" karakter

Ketika \bterlihat OK dan panjangnya tidak masalah seperti masuk ke konsol, ini sepertinya cukup baik untuk saya.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.