如何从 StringBuilder 中删除最后一个字符?

528
当你需要遍历一个集合并将每个数据用分隔符分开组成一个字符串时,最后总会多余一个分隔符,例如:
for (String serverId : serverIds) {
  sb.append(serverId);
   sb.append(",");
}

结果类似于: serverId_1, serverId_2, serverId_3,

我想在StringBuilder中删除最后一个字符(不转换,因为我在此循环之后仍需要它)。


11
如果加入字符串意味着“字符串拼接”,那么它取决于字符串的数量和长度。如果您将要连接大量字符串,而且这些字符串的大小不确定,使用字符串生成器会更有效率,因为字符串是不可变的。每次连接字符串时,都会创建一个新的结果字符串(实际上是一个字符数组)。字符串生成器本质上是一个字符列表,在调用toString()方法之前不会成为不可变的字符串。 - dyslexicanaboko
7
如果您在使用Java 8,则只需使用StringJoiner:https://dev59.com/enA75IYBdhLWcg3wP2kg#29169233 - ArtOfWarfare
19个回答

741

其他人提到了使用deleteCharAt方法,但这里还有另一种替代的方法:

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

或者,你可以使用来自GuavaJoiner类 :)

从Java 8开始,StringJoiner是标准JRE的一部分。


9
不行,因为 "" 是没有任何字符,而不是一个单独的字符。 - Jon Skeet
42
每次循环执行 prefix=","; 不会影响性能吗? - Harish
25
可能有一点点,但很不可能对结果产生重大影响。 - Jon Skeet
7
如果优化器展开第一个循环迭代,@Harish可能根本不需要执行。 - Stephen C
7
Apache Commons中的StringUtils提供了另一种替代Guava的Joiner的选择,可以使用join方法。具体请参考链接:http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringUtils.html#join(java.lang.Object[], java.lang.String) - GoRoS
显示剩余9条评论

547

另一个简单的解决方案是:

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

33
非常好的解决方案。对性能影响最小,需要的代码最少 :) - Alain O'Dea
1
@Stephen C @Alain O'Dea,我无法理解这里的性能提升,它在内部执行了一个数组复制selLenghth() -> ensureCapacityInternal() -> Arrays.copyOf -> System.arraycopy()。 - Manu Jose
2
@ManuJose - 仔细阅读代码。如果setLength正在减少长度,则ensureCapacityInternal将不会调用Arrays.copy。(我正在查看Java 11代码,但我认为这适用于所有版本。) - Stephen C

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

45
这个帖子得到了太多的点赞,但它不够高效,它使用了system.arraycopy。就像@Rohit Reddy Korrapolu所说的那样。 - alianos-
在使用代理对字符时,这是否安全? - rogerdpack
1
假设最后一个字符是逗号分隔符(如示例所示),那么代理没有任何影响。如果需要概括,则应该减去“separator.length()”而不是1。 - Stephen C

68

从Java 8开始,String类有一个静态方法join。第一个参数是你想在每对字符串之间插入的字符串,第二个参数是Iterable<CharSequence>(它们都是接口,因此像List<String>这样的东西也可以用)。所以你可以这样做:

String.join(",", serverIds);

在Java 8中,您还可以使用新的StringJoiner类来处理这种情况:您希望在拥有要放入其中的所有元素之前开始构造字符串。


如果您不介意的话,我为您编辑了nvm,并删除了我的评论。 - Eugene
@Eugene - 我完全重写了答案,将重点放在String.join上,而不是StringJoiner - ArtOfWarfare

49

只需获取最后一个字符出现的位置即可。

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

输出:

Logan-Magneto-Rogue-Storm

带前缀和后缀的示例:

    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,Rick,Maggie,Daryl)


1
你可以确信最后一个字符是逗号 , 因为它是 for循环 的最后一条语句。使用 lastIndexOf 更多是为了可读性和使其变得更容易,如果你不想记住它是否以0为索引。此外,你无需修改StringBuilder的长度。这只是为了方便而已。 - Reuel Ribeiro

40

在这种情况下,

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

更好的方法是直接将最后一个值分配给'\0',而不是删除最后一个字符时使用System.arraycopy


1
setLength 方法没有向最后一个值分配任何内容。Java 字符串缓冲区不是 null/零终止的。实际上,setLength 只是更新了一个 length 字段。 - Stephen C
@Rohit Reddy Korrapolu:但是arraycopy复制了0个元素,所以我猜它可以被优化掉。 - maaartinus
2
如果newLength参数大于或等于当前长度,则追加足够的null字符('\u0000'),使长度变为newLength参数。但这不是情况。 - fglez

12

10

另一种选择

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

3
最好不要删掉最后一个字符,因为这需要进行大小计算。除非删除第一个字符会导致数据被移动... - slott

9

或者,

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

可用,只需在末尾添加一个“.”。 - anthonyms

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接