删除StringBuilder的最后一个字符?


422

当您必须遍历一个集合并用分隔符分隔每个数据的字符串时,总是总是在末尾使用一个额外的分隔符,例如

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

类似于:serverId_1,serverId_2,serverId_3,

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


11
如果通过连接字符串来表示“字符串连接”,则取决于字符串的数量及其长度。如果您要锤击很多字符串而不管它们的大小,则使用字符串生成器会更有效率,因为字符串是不可变的。每次将字符串连接在一起时,都会创建一个新的结果字符串(这实际上是一个char数组)。字符串构建器本质上是一个char列表,​​在调用toString()方法之前,char不会成为不可变的字符串。
dyslexicanaboko 2012年

4
如果您使用的是Java 8,只需使用StringJoinerstackoverflow.com/a/29169233/901641
ArtOfWarfare 2015年

Answers:


640

其他人指出了该deleteCharAt方法,但这是另一种替代方法:

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

或者,使用Guava中Joiner类:)

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


7
@Coronatus:否,因为“” 包含任何字符,而不是单个字符。
乔恩·斯基特

31
不会执行prefix =“,”; 每个循环都会影响性能吗?
哈里什

21
@Harish:可能是一小部分,尽管不太可能有意义。
乔恩·斯基特

4
@Harish-如果优化器展开第一个循环迭代,则可能根本没有。
斯蒂芬·C

6
Apache的百科全书确实有另一种选择,以番石榴是Joiner太他们StringUtilscommons.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- 2012年

13
它是不安全的sb.length() == 0也是
马蒂亚斯

使用代理对字符是否安全?
rogerdpack 2015年

假设最后一个字符是逗号分隔符(按照示例),则替代项没有任何区别。如果您需要推广,然后减去separator.length()而不是1
斯蒂芬ç

61

从Java 8开始,String类具有静态方法join。第一个参数是您希望在每对字符串之间使用的字符串,第二个参数是Iterable<CharSequence>(两者都是接口,因此类似List<String>工作。因此,您可以执行以下操作:

String.join(",", serverIds);

同样在Java 8中,StringJoiner对于在要放置字符串的完整列表之前要开始构造字符串的情况,可以使用new 类。


如果您不介意的话,nvm为您编辑,还删除了我的评论
Eugene,

@Eugene-我完全重写了答案,String.join而不是专注于StringJoiner
ArtOfWarfare

37

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

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

由于lastIndexOf将执行反向搜索,并且您知道它将在第一次尝试中找到,因此此处性能不会成为问题。

编辑

由于我一直在回答自己的问题(感谢folk),因此值得考虑:

Java 8开始,使用StringJoiner会更清晰易懂。它具有一种用于简单分隔符的方法,以及用于前缀和后缀的重载。

示例取自此处:example

使用简单分隔符的示例:

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

输出:

洛根·马涅托·罗格·斯托姆

带后缀和前缀的示例:

    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)


您将确保最后一个字符是a,,因为它是的最后一个语句for loop。的lastInfexOf是更多的可读性,使它成为一个没有脑子,如果你不想记住,如果是0,索引或没有。此外,您不需要干预stringbuilder的长度。只是为了方便。
Reuel Ribeiro

34

在这种情况下,

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

最好是,因为它只是将最后一个值赋给,'\0'而删除最后一个字符却System.arraycopy


1
setLength调用未为最后一个值分配任何内容。Java字符串缓冲区不以null /零终止。实际上,setLength只是在更新一个length字段。
斯蒂芬·C

@Rohit Reddy Korrapolu:但是arraycopy复制了0个元素,所以我想可以对其进行优化。
maaartinus

2
如果newLength参数大于或等于当前长度,则将附加足够的空字符('\ u0000'),以便长度成为newLength参数。事实并非如此。
fglez 2014年


11

另一种选择

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

2
应该比删除最后一个字符更好,因为这需要计算大小。除非删除第一个字符,否则数据将四处移动...
时隙

8

或者,

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

可用,因为您可以添加“。” 在最后。
artfullyContrived

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

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

为了避免重新prefix使用TextUtils.isEmpty (影响性能) :

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

TestUtils属于哪种软件包?
马库斯

@Markus android.text.TextUtils
NickUnuchek

1

您可以尝试使用“ Joiner”类,而不是从生成的文本中删除最后一个字符;

                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上调用子字符串而不是字符串。
斯蒂芬·C

但是无论如何,这只是Zaki自2010
Stephen C

0

我个人喜欢在末尾添加一个退格字符(或更长的“定界符”)。

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

sb.append('\b');

请注意,它有问题:

  • 如何\b显示取决于环境,
  • length()中的String内容可能是从“分享范围”字符的长度不同

\b看起来不错并且长度无关紧要(例如登录到控制台)时,这对我来说似乎足够好。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.