ลบอักขระสุดท้ายของ StringBuilder หรือไม่


423

เมื่อคุณต้องวนซ้ำการรวบรวมและสร้างสตริงของข้อมูลแต่ละตัวคั่นด้วยตัวคั่นคุณจะต้องจบด้วยตัวคั่นพิเศษที่ส่วนท้ายเช่น

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

ให้สิ่งที่ต้องการ: serverId_1, serverId_2, serverId_3,

ฉันต้องการลบอักขระตัวสุดท้ายใน StringBuilder (โดยไม่ต้องแปลงเพราะฉันยังต้องการหลังจากลูปนี้)


11
หากการรวมสตริงคุณหมายถึง "string concatenation" ขึ้นอยู่กับจำนวนสตริงและความยาว การใช้ตัวสร้างสตริงจะมีประสิทธิภาพมากกว่าถ้าคุณใช้ค้อนทุบในสตริงจำนวนมากโดยไม่คำนึงถึงขนาดของสตริงเนื่องจากสตริงนั้นไม่เปลี่ยนรูป ทุกครั้งที่คุณเชื่อมสตริงเข้าด้วยกันคุณกำลังสร้างสตริงผลลัพธ์ใหม่ (ซึ่งเป็นอาร์เรย์ถ่าน) ผู้สร้างสตริงนั้นเป็นรายการ char ที่ไม่ได้กลายเป็นสตริงที่ไม่เปลี่ยนรูปจนกว่าคุณจะเรียกเมธอด toString ()
dyslexicanaboko

4
หากคุณกำลังใช้งาน Java 8 เพียงใช้งานStringJoiner: stackoverflow.com/a/29169233/901641
ArtOfWarfare

คำตอบ:


640

คนอื่น ๆ ได้ชี้ให้เห็นdeleteCharAtวิธีการ แต่นี่เป็นอีกทางเลือกหนึ่ง:

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

หรือใช้JoinerคลาสจากGuava :)

ในฐานะของ Java 8 StringJoinerเป็นส่วนหนึ่งของ JRE มาตรฐาน


7
@Coronatus: ไม่เพราะ "" ไม่มีตัวอักษรใด ๆ ไม่ใช่ตัวเดียว
Jon Skeet

31
จะไม่ดำเนินการคำนำหน้า = ","; ทุกรอบวงมีผลต่อประสิทธิภาพหรือไม่
Harish

21
@Harish: อาจเป็นบิตเล็ก ๆ - ไม่น่าจะมีนัยสำคัญแม้ว่า
Jon Skeet

4
@Harish - และอาจเป็นไปไม่ได้เลยหากเครื่องมือเพิ่มประสิทธิภาพยกเลิกการวนซ้ำลูปแรก
สตีเฟ่นซี

6
Apache Commons มีทางเลือกอื่นให้กับ Guava Joinerเช่นStringUtilsกัน commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… , java.lang.String)
GoRoS

419

อีกวิธีง่ายๆคือ:

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

ทางออกที่ซับซ้อนมากขึ้น:

วิธีแก้ปัญหาข้างต้นจะถือว่าsb.length() > 0... นั่นคือมี "อักขระตัวสุดท้าย" ที่จะลบ หากคุณไม่สามารถทำการสันนิษฐานนั้นและ / หรือคุณไม่สามารถจัดการกับข้อยกเว้นที่จะตามมาหากการสันนิษฐานไม่ถูกต้องให้ตรวจสอบความยาวของ StringBuilder ก่อน เช่น

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

หรือ

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

23
ทางออกที่ดีมาก ส่งผลกระทบต่อประสิทธิภาพการทำงานต่ำสุดและต้องการรหัสน้อยที่สุด :)
Alain O'Dea

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

33
สิ่งนี้ได้รับการโหวตมากเกินไป แต่ก็ไม่มีประสิทธิภาพมันทำ system.arraycopy @Rohit Reddy Korrapolu พูดอะไร
alianos-

13
มันไม่ปลอดภัยsb.length() == 0ด้วยเช่นกัน
Matthias

ปลอดภัยหรือไม่เมื่อใช้ตัวละครคู่แทน
rogerdpack

สมมติว่าตัวอักษรตัวสุดท้ายเป็นตัวคั่นจุลภาค (ตามตัวอย่าง) จากนั้นตัวแทนจะไม่สร้างความแตกต่าง หากคุณจำเป็นต้องพูดคุยแล้วลบออกseparator.length()แทน 1
สตีเฟนซี

61

ในฐานะของ Java 8 ชั้น String joinมีวิธีการคงที่ อาร์กิวเมนต์แรกคือสตริงที่คุณต้องการระหว่างสตริงแต่ละคู่และข้อที่สองคือIterable<CharSequence>(ซึ่งเป็นทั้งสองอินเตอร์เฟสดังนั้นมีอะไรบางอย่างที่เหมือนList<String>ผลงานดังนั้นคุณสามารถทำสิ่งนี้ได้:

String.join(",", serverIds);

นอกจากนี้ใน Java 8 คุณสามารถใช้StringJoinerคลาสใหม่สำหรับสถานการณ์ที่คุณต้องการเริ่มการสร้างสตริงก่อนที่คุณจะมีรายการองค์ประกอบทั้งหมดเพื่อใส่ไว้ในนั้น


แก้ไข nvm ให้คุณถ้าคุณไม่รังเกียจเอาความคิดเห็นของฉันออกด้วย
ยูจีน

@Eugene - ฉันเขียนคำตอบที่สมบูรณ์ที่จะมุ่งเน้นแทนString.join StringJoiner
ArtOfWarfare

37

เพิ่งได้รับตำแหน่งของตัวละครตัวสุดท้ายที่เกิดขึ้น

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

เนื่องจากlastIndexOfจะทำการค้นหาแบบย้อนกลับและคุณรู้ว่าจะสามารถค้นหาได้ตั้งแต่ครั้งแรกประสิทธิภาพจะไม่มีปัญหาที่นี่

แก้ไข

เนื่องจากฉันได้รับการตอบสนองต่อไปเรื่อย ๆ (ขอบคุณผู้คน😊) มันมีค่ามากที่:

บนJava 8เป็นต้นไปมันก็จะชัดเจนมากขึ้นและชัดเจนที่จะใช้StringJoiner มันมีวิธีการหนึ่งสำหรับคั่นง่าย ๆ และเกินสำหรับคำนำหน้าและคำต่อท้าย

ตัวอย่างที่นำมาจากที่นี่: ตัวอย่าง

ตัวอย่างการใช้ตัวคั่นอย่างง่าย:

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

เอาท์พุท:

โลแกน-แม๊ก-Rogue พายุ

ตัวอย่างที่มีคำต่อท้ายและคำนำหน้า:

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

เอาท์พุต

(Negan ริกแม็กกี้, ดาริล)


คุณจะต้องแน่ใจว่าตัวละครที่ผ่านมาเป็นเพราะมันเป็นคำสั่งสุดท้ายของ, มากขึ้นสำหรับการอ่านและการที่จะทำให้มันไม่มีเกมง่ายๆถ้าคุณไม่ต้องการที่จะจำถ้ามันเป็น 0 การจัดทำดัชนีหรือไม่ นอกจากนี้คุณไม่จำเป็นต้องเข้าไปยุ่งกับความยาวของตัวสร้างสตริง มันเป็นเพียงเพื่อความสะดวก for looplastInfexOf
Reuel Ribeiro

34

ในกรณีนี้,

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

เป็นที่นิยมมากกว่าเพราะเพิ่งกำหนดค่าสุดท้ายให้'\0'ในขณะที่การลบอักขระตัวสุดท้ายทำSystem.arraycopy


1
การsetLengthโทรไม่ได้กำหนดอะไรให้เป็นค่าสุดท้าย บัฟเฟอร์สตริง Java ไม่ได้เป็นค่าสิ้นสุด / ศูนย์ ในความเป็นจริงsetLengthเพียงแค่อัปเดตlengthฟิลด์
สตีเฟ่นซี

@Rohit Reddy Korrapolu: แต่arraycopyองค์ประกอบที่คัดลอก 0 ดังนั้นฉันเดาว่ามันสามารถเพิ่มประสิทธิภาพออกไป
maaartinus

2
หากอาร์กิวเมนต์ newLength มากกว่าหรือเท่ากับความยาวปัจจุบันจะมีการเพิ่มอักขระ null ที่เพียงพอ ('\ u0000') เพื่อให้ความยาวนั้นกลายเป็นอาร์กิวเมนต์ newLength ซึ่งไม่ใช่กรณี
fglez


11

ทางเลือกอื่น

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

2
ควรจะดีกว่าการลบอักขระตัวสุดท้ายเนื่องจากต้องใช้การคำนวณขนาด ถ้าไม่ลบถ่านตัวแรกจะทำให้ข้อมูลถูกย้ายไปรอบ ๆ ...
slott

8

อีกวิธีหนึ่งคือ

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

ใช้งานได้ในขณะที่คุณสามารถเพิ่ม "." ในตอนท้าย
artrivContrived


5

อีกทางเลือกอื่น:

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

เพื่อหลีกเลี่ยงการ reinit (ส่งผลกระทบต่อประสิทธิภาพการprefixใช้งาน) TextUtils.isEmpty:

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

TestUtils เป็นแพ็คเกจประเภทใด
Markus

@Markus android.text.TextUtils
NickUnuchek

1

คุณอาจลองใช้คลาส 'ช่างไม้' แทนการลบอักขระตัวสุดท้ายออกจากข้อความที่คุณสร้างขึ้น

                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

ฉันกำลังทำสิ่งที่ชอบด้านล่าง:

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

0

นี่คือทางออกอื่น:

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

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

1
อ้อเข้าใจแล้ว. คุณกำลังเรียกซับสตริงบน StringBuilder ไม่ใช่สตริง
Stephen C

แต่อย่างไรก็ตามนี่เป็นเพียงตัวแปรเล็ก ๆ สำหรับวิธีแก้ปัญหาของ Zaki ตั้งแต่ปี 2010
Stephen C

0

ฉันชอบที่จะผนวกอักขระแบ็คสเปซ (หรือมากกว่านั้นสำหรับ "ตัวคั่น" ที่ยาวกว่า) ในตอนท้าย:

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

sb.append('\b');

โปรดทราบว่ามันมีปัญหา:

  • วิธีการ\bแสดงขึ้นอยู่กับสภาพแวดล้อม
  • length()ของStringเนื้อหาอาจจะแตกต่างจากความยาวของตัวละคร "ที่เห็นว่า"

เมื่อ\bดูตกลงและความยาวไม่สำคัญเช่นการเข้าสู่คอนโซลสิ่งนี้ดูดีพอสำหรับฉัน


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.