Xóa ký tự cuối cùng của StringBuilder?


423

Khi bạn phải lặp qua một bộ sưu tập và tạo một chuỗi mỗi dữ liệu được phân tách bằng dấu phân cách, bạn luôn kết thúc bằng một dấu phân cách bổ sung ở cuối, ví dụ:

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

Cung cấp một cái gì đó như: serverId_1, serverId_2, serverId_3,

Tôi muốn xóa ký tự cuối cùng trong StringBuilder (không chuyển đổi nó vì tôi vẫn cần nó sau vòng lặp này).


11
Nếu bằng cách nối các chuỗi, bạn có nghĩa là "nối chuỗi", nó phụ thuộc vào số lượng chuỗi và độ dài của chúng. Sử dụng một trình tạo chuỗi sẽ hiệu quả hơn nếu bạn định gõ vào rất nhiều chuỗi bất kể kích thước của chúng vì các chuỗi là bất biến. Mỗi khi bạn nối các chuỗi lại với nhau, bạn sẽ tạo một chuỗi kết quả mới (thực sự là một mảng char). Các trình tạo chuỗi về cơ bản là một danh sách các char không trở thành một chuỗi bất biến cho đến khi bạn gọi phương thức toString ().
chứng khó đọc

4
Nếu bạn đang sử dụng Java 8, chỉ cần sử dụng StringJoiner: stackoverflow.com/a/29169233/901641
ArtOfWarfare 10/07/2015

Câu trả lời:


640

Những người khác đã chỉ ra deleteCharAtphương pháp này, nhưng đây là một cách tiếp cận khác:

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

Ngoài ra, sử dụng Joinerlớp từ Guava :)

Kể từ Java 8, StringJoinerlà một phần của JRE tiêu chuẩn.


7
@Coronatus: Không, vì "" là sự vắng mặt của bất kỳ ký tự nào, không phải là một ký tự.
Jon Skeet

31
sẽ không thực hiện tiền tố = ","; mỗi chu kỳ vòng lặp ảnh hưởng đến hiệu suất?
Harish

21
@Harish: Có thể, một chút, rất nhỏ - rất khó có thể là đáng kể mặc dù.
Jon Skeet

4
@Harish - và có thể hoàn toàn không, nếu trình tối ưu hóa hủy bỏ vòng lặp đầu tiên.
Stephen C

6
Apache Commons không có lựa chọn khác để Ổi là Joinerquá trong họ StringUtils. commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/iêu , java.lang.String)
GoRoS

419

Một giải pháp đơn giản khác là:

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

Một giải pháp phức tạp hơn:

Giải pháp trên giả định rằng sb.length() > 0... tức là có "ký tự cuối cùng" cần xóa. Nếu bạn không thể đưa ra giả định đó và / hoặc bạn không thể đối phó với ngoại lệ sẽ xảy ra nếu giả định đó không chính xác, trước tiên hãy kiểm tra độ dài của StringBuilder; ví dụ

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

hoặc là

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

23
Giải pháp rất hay. Tác động thấp nhất đến hiệu suất và yêu cầu ít mã nhất :)
Alain O'Dea

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

33
Điều này được nâng cấp quá nhiều nhưng nó không hiệu quả, nó thực hiện một system.arraycopy. Những gì @Rohit Reddy Korrapolu nói.
alianos-

13
Đó là không an toàn cho sb.length() == 0cũng
Matthias

Điều này có an toàn với các nhân vật cặp thay thế khi chơi không?
rogerdpack 18/03/2015

Giả sử rằng ký tự cuối cùng là dấu phân cách dấu phẩy (theo ví dụ), thì người thay thế không có sự khác biệt. Nếu bạn cần khái quát thì trừ đi separator.length()thay vì 1.
Stephen C

61

Kể từ Java 8, lớp String có một phương thức tĩnh join. Đối số đầu tiên là một chuỗi mà bạn muốn giữa mỗi cặp chuỗi và chuỗi thứ hai là một Iterable<CharSequence>(cả hai giao diện, vì vậy một cái gì đó giống như List<String>hoạt động. Vì vậy, bạn có thể làm điều này:

String.join(",", serverIds);

Cũng trong Java 8, bạn có thể sử dụng StringJoinerlớp mới , cho các kịch bản mà bạn muốn bắt đầu xây dựng chuỗi trước khi bạn có danh sách đầy đủ các phần tử để đặt vào nó.


nvm đã chỉnh sửa cho bạn nếu bạn không phiền, cũng xóa bình luận của tôi
Eugene

@Eugene - Tôi viết lại câu trả lời hoàn toàn để tập trung vào String.jointhay vì StringJoiner.
ArtOfWarfare

37

Chỉ cần có được vị trí của nhân vật cuối cùng xảy ra.

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

lastIndexOfsẽ thực hiện tìm kiếm ngược và bạn biết rằng nó sẽ tìm thấy ở lần thử đầu tiên, hiệu suất sẽ không phải là vấn đề ở đây.

BIÊN TẬP

Vì tôi tiếp tục nhận được câu trả lời của mình (cảm ơn mọi người), nên có giá trị về điều đó:

Trên Java 8 trở đi, việc sử dụng StringJoiner sẽ dễ đọc và rõ ràng hơn . Nó có một phương thức cho một dấu tách đơn giản và quá tải cho tiền tố và hậu tố.

Ví dụ lấy từ đây: ví dụ

Ví dụ sử dụng dấu phân cách đơn giản:

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

Đầu ra:

Logan-Magneto-Rogue-Bão

Ví dụ với hậu tố và tiền tố:

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

Đầu ra

(Negan, Rick, Maggie, Daryl)


Bạn sẽ chắc chắn rằng nhân vật cuối cùng là ,bởi vì đó là tuyên bố cuối cùng của for loop. Nhiều lastInfexOfhơn cho khả năng đọc và làm cho nó không có trí tuệ nếu bạn không muốn nhớ nếu nó là 0-index hay không. Hơn nữa, bạn không cần phải can thiệp với chiều dài chuỗi. Nó chỉ để thuận tiện.
Reuel Ribeiro

34

Trong trường hợp này,

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

tốt hơn là nó chỉ gán giá trị cuối cùng '\0'trong khi xóa ký tự cuối cùngSystem.arraycopy


1
Cuộc setLengthgọi không chỉ định bất cứ điều gì cho giá trị cuối cùng. Bộ đệm chuỗi Java không kết thúc null / zero. Trong thực tế, setLengthchỉ đơn giản là cập nhật một lengthlĩnh vực.
Stephen C

@Rohit Reddy Korrapolu: Nhưng các arraycopybản sao 0 phần tử, vì vậy tôi đoán nó có thể được tối ưu hóa đi.
maaartinus

2
Nếu đối số bước sóng mới lớn hơn hoặc bằng độ dài hiện tại, đủ các ký tự null ('\ u0000') sẽ được thêm vào để độ dài trở thành đối số bước sóng mới. Đó không phải là trường hợp.
fglez


11

Một cách khác

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

2
Nên tốt hơn là loại bỏ char cuối cùng vì điều này đòi hỏi tính toán kích thước. Trừ khi loại bỏ char đầu tiên khiến dữ liệu bị di chuyển xung quanh ...
slott 17/07/13

8

Ngoài ra,

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

Có thể sử dụng khi bạn có thể thêm "." cuối cùng.
artlyContrived

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

5

Một lựa chọn khác:

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

Để tránh reinit (ảnh hưởng đến hiệu suất) của prefixviệc sử dụng TextUtils.isEmpty:

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

TestUtils thuộc loại gói nào?
Markus

@Markus android.text.TextUtils
NickUnuchek

1

Bạn có thể thử sử dụng lớp 'Người tham gia' thay vì xóa ký tự cuối cùng khỏi văn bản đã tạo của bạn;

                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

Tôi đang làm một cái gì đó như dưới đây:

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

0

Đây là một giải pháp khác:

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

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

1
Ồ ... tôi hiểu rồi. Bạn đang gọi chuỗi con trên StringBuilder chứ không phải String.
Stephen C

Nhưng dù sao, đây chỉ là một biến thể nhỏ trên giải pháp của Zaki từ năm 2010
Stephen C

0

Cá nhân tôi muốn nối thêm một ký tự backspace (hoặc nhiều hơn cho "dấu phân cách" dài hơn) ở cuối:

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

sb.append('\b');

Lưu ý rằng nó có vấn đề:

  • Làm thế nào \bđược hiển thị phụ thuộc vào môi trường,
  • các length()của Stringnội dung có thể khác nhau từ chiều dài của nhân vật "visibile"

Khi \btrông ổn và độ dài không thành vấn đề như đăng nhập vào bảng điều khiển, điều này có vẻ đủ tốt với tôi.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.