移除StringBuilder的最后一个字符?

当你必须遍历一个集合并用分隔符将每个数据组成一个字符串时,你总是会在最后得到一个额外的分隔符,例如:

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

给出类似serverId_1, serverId_2, serverId_3,的值

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

435867 次浏览
if(sb.length() > 0){
sb.deleteCharAt(sb.length() - 1);
}

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

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));
StringBuilder sb = new StringBuilder();
sb.append("abcdef");
sb.deleteCharAt(sb.length() - 1);
assertEquals("abcde",sb.toString());
// true

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

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

或者,使用番石榴中的Joiner类:)

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

另外,

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

还有另一种选择:

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

另一种替代方法

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

在这种情况下,

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

是可取的,因为它只是将最后一个值分配给'\0',而删除最后一个字符System.arraycopy

从Java 8开始,String类有一个静态方法join。第一个参数是每对字符串之间的字符串,第二个参数是Iterable<CharSequence>(两者都是接口,所以类似List<String>的东西可以工作。所以你可以这样做:

String.join(",", serverIds);

同样在Java 8中,你可以使用新的StringJoiner类,用于你想在你有完整的元素列表之前开始构造字符串的场景。

我正在做的事情如下:

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

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

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

因为lastIndexOf将执行反向搜索,而且你知道它会在第一次尝试时找到,所以性能在这里不是问题。

编辑

由于我一直在回答(谢谢大家😊),值得注意的是:

< em > Java 8 < / em >之后,使用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玛吉,达里尔)

对于Java-8,你可以使用String类的静态方法,

String#join(CharSequence delimiter,Iterable<? extends CharSequence> elements). .


public class Test {


public static void main(String[] args) {


List<String> names = new ArrayList<>();
names.add("James");
names.add("Harry");
names.add("Roy");
System.out.println(String.join(",", names));
}
}

输出

James,Harry,Roy

为了避免prefix的reinit(影响性能),请使用TextUtils.isEmpty:

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

您可以尝试使用'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"

这里有另一个解决方案:

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


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

stringBuilder.Remove (stringBuilder。长度- 1,1);

我发现自己这样做了很多,所以我为3个主要的附加分隔符技术写了一个基准测试:

(具有适当的预热和100轮100,000次迭代的基准测试)

“附加After"

static void appendAfter()
{
sb.append('{');
for (int i = 0; i < 10; i++)
{
sb.append('"');
sb.append(i);
sb.append('"');
sb.append(':');
sb.append(i);
sb.append(',');
}
sb.setLength(sb.length() - 1);
sb.append('}');
}

“附加Before"

static void appendBefore()
{
sb.append('{');
String delimiter = "";
for (int i = 0; i < 10; i++)
{
sb.append(delimiter);
sb.append('"');
sb.append(i);
sb.append('"');
sb.append(':');
sb.append(i);
delimiter = ",";
}
sb.append('}');
}

“附加Maybe"

static void appendMaybe()
{
sb.append('{');
for (int i = 0; i < 10; i++)
{
sb.append('"');
sb.append(i);
sb.append('"');
sb.append(':');
sb.append(i);
if (i < 9)
{
sb.append(',');
}
}
sb.append('}');
}

我得到了以下结果:

平台 添加后 添加之前 附加也许
Windows Server 2016, Java 11 -热点 26岁女士 40毫秒 26岁女士
Windows Server 2016, Java 8 -热点 27岁的女士 36岁的女士 21岁的女士
Windows Server 2016, Java 11 - OpenJ9 63毫秒 81毫秒 59岁的女士
Windows Server 2016, Java 8 - OpenJ9 66毫秒 64毫秒 55岁的女士

除了最快之外,我还认为“追加可能”;实现最好地展示了代码的意图。这通常比每次迭代获得的纳秒分数更重要。

我留下了基准代码在这里,以防有人想在他们的平台上尝试它。如果你这样做,请贡献你的结果!