删除StringBuilder/StringBuffer中的最后一个字符,deleteCharAt或setLength哪种方法更好?

17
在许多情况下,我们需要删除 StringBuilder/StringBuffer 的最后一个字符。例如,给定一个 int[]{1,2,3},实现一个String toString(int[] a)方法,将每个元素用逗号隔开进行连接。输出应该是1,2,3,没有尾随逗号。
我们可以很容易地编写一个循环:
int[] nums = new int[]{1,2,3,4,5};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nums.length; i++) {
    sb.append(nums[i]);
    sb.append(",");
}
//here we need to remove the tailing ','

但是我们始终需要删除末尾的','。有两种实现方法:

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

sb.setLength(sb.length() - 1);
哪个更值得推荐?为什么?
注意: 我知道Arrays.toString是做什么的。这只是一个例子,用于描述我的问题,可能不太合适。 这不是关于字符串拼接的讨论,而是关于StringBuffer/StringBuilder最佳实践的。

Arrays.toString(nums)有什么问题?然后删除第一个和最后一个字符。但这是一个关于一般使用的好问题。 - Anirban Nag 'tintinmj'
@tintinmj:同意你的观点。我已经编辑了我的问题。 - Weibo Li
我在CodeReview.SE上找到了一个与你的问题非常相似的问题。 - Anirban Nag 'tintinmj'
@jon-skeet的这个答案提供了一个非常好的替代方案,可以在不删除任何尾部的情况下使用StringBuilder。 - J.A.I.L.
4个回答

17

实际上,它并没有太多内容,可能取决于硬件和其他因素。

setLength() 方法只是改变计数,并用零字节覆盖数组中不需要的值。

deleteCharAt() 在更改计数之前在内部执行数组复制。听起来很戏剧化,但被复制的数组实际上是零长度的,因为您正在删除最后一个字符。

我建议选择setLength(),因为它打字更短,而且我认为这样做会更清晰地表明您正在做什么。 如果性能是一个问题,并且在测量时发现这是您的瓶颈,那么也许您可以考虑使用不需要改变大小的不同算法(例如JB Nizet的答案)。


3
如果你使用任何集成开发环境(IDE),那么“更短的键入时间”就不是问题了。 - Anirban Nag 'tintinmj'
2
+1:两者成本都可以忽略不计。使用你认为更清晰的方式。 - JB Nizet

2
正确的做法是有条件地在逗号前加上内容:
for (int i = 0; i < nums.length; i++) {
    if (i > 0)
        sb.append(',');
    sb.append(nums[i]);
}

那么你就不需要担心删除最后一个字符,因为它已经是正确的了。


1

我不会这样做。相反,如果元素不是数组的最后一个元素,我只会添加尾随逗号。或者我会使用Guava的Joiner(或Apache-commons StringUtils),这样可以使代码更加清晰:

String s = Joiner.on(',').join(nums);

注意:我刚刚注意到Guava的Joiner不能处理原始数组。无论如何,您应该从上面得到这个想法。

谢谢您的回答。但是这不是关于联系字符串的讨论,而是关于StringBuffer/StringBuilder的最佳实践。 - Weibo Li
1
对我来说,最佳实践是避免附加那些你知道以后必须删除的内容。也许你应该想出一个真正必要的用例。在多年的Java编程中,我无法记得曾经不得不这样做过。 - JB Nizet

0
如果您需要更快的速度,您的用例可能允许您只保留自己的变量。
int activelength;

然后你可以使用这个操作

activelength--;

这将比原来更快

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

这会花费额外的时间来清除字节或字,以提高安全性。

当然,你的上下文将决定这对你是否是一个胜利,因为其他你可能需要的操作,比如扩展StringBuilder,需要一些额外的逻辑来检测和调用。


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