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

Matius
sumber
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.

Jon Skeet
sumber
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));
Stephen C
sumber
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);
}
pembual
sumber
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.

ArtOfWarfare
sumber
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)

Reuel Ribeiro
sumber
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

Rohit Reddy Korrapolu
sumber
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);
Rafiq
sumber
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) ;
Zaki
sumber
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
Antoine
sumber
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();
}
Jason Day
sumber
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);
            }
NickUnuchek
sumber
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"
oguzhan
sumber
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(", ");
        }
    }
Vikasdeep Singh
sumber
0

Ini solusi lain:

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

String resultingString = "";
if ( sb.length() > 1 ) {
    resultingString = sb.substring(1);
}
Stephan
sumber
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.

Attacktive
sumber
-1

stringBuilder.Hapus (stringBuilder.Panjang - 1, 1);

Mohamed Farook Mohamed Fazrin
sumber