Remove last character of a StringBuilder?

JavaStringbuilder

Java Problem Overview


When you have to loop through a collection and make a string of each data separated by a delimiter, you always end up with an extra delimiter at the end, e.g.

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

Gives something like : serverId_1, serverId_2, serverId_3,

I would like to delete the last character in the StringBuilder (without converting it because I still need it after this loop).

Java Solutions


Solution 1 - Java

Others have pointed out the deleteCharAt method, but here's another alternative approach:

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

Alternatively, use the Joiner class from Guava :)

As of Java 8, StringJoiner is part of the standard JRE.

Solution 2 - Java

Another simple solution is:

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

A more complicated solution:

The above solution assumes that sb.length() > 0 ... i.e. there is a "last character" to remove. If you can't make that assumption, and/or you can't deal with the exception that would ensue if the assumption is incorrect, then check the StringBuilder's length first; e.g.

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

or

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

Solution 3 - Java

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

Solution 4 - Java

As of Java 8, the String class has a static method join. The first argument is a string that you want between each pair of strings, and the second is an Iterable<CharSequence> (which are both interfaces, so something like List<String> works. So you can just do this:

String.join(",", serverIds);

Also in Java 8, you could use the new StringJoiner class, for scenarios where you want to start constructing the string before you have the full list of elements to put in it.

Solution 5 - Java

Just get the position of the last character occurrence.

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

Since lastIndexOf will perform a reverse search, and you know that it will find at the first try, performance won't be an issue here.

EDIT

Since I keep getting ups on my answer (thanks folks ), it is worth regarding that:

On Java 8 onward it would just be more legible and explicit to use StringJoiner. It has one method for a simple separator, and an overload for prefix and suffix.

Examples taken from here: example

Example using simple separator:

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

Output:

> Logan-Magneto-Rogue-Storm

Example with suffix and prefix:

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

Output

> (Negan,Rick,Maggie,Daryl)

Solution 6 - Java

In this case,

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

is preferable as it just assign the last value to '\0' whereas deleting last character does System.arraycopy

Solution 7 - Java

With Java-8 you can use static method of String class,

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

OUTPUT

James,Harry,Roy

Solution 8 - Java

Another alternative

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

Solution 9 - Java

Alternatively,

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

Solution 10 - Java

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



Solution 11 - Java

Yet another alternative:

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

Solution 12 - Java

To avoid reinit(affect performance) of prefix use TextUtils.isEmpty:

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

Solution 13 - Java

I am doing something like below:

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

Solution 14 - Java

You may try to use 'Joiner' class instead of removing the last character from your generated text;

                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"

Solution 15 - Java

Here is another solution:

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

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

Solution 16 - Java

stringBuilder.Remove(stringBuilder.Length - 1, 1);

Solution 17 - Java

I found myself doing this quite a bit so I wrote a benchmark for the 3 main append delimiter techniques:

(benchmark with proper warmup and 100 rounds of 100,000 iterations)

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

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

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

I got the following results:

Platform Append After Append Before Append Maybe
Windows Server 2016, Java 11 - Hotspot 26ms 40ms 26ms
Windows Server 2016, Java 8 - Hotspot 27ms 36ms 21ms
Windows Server 2016, Java 11 - OpenJ9 63ms 81ms 59ms
Windows Server 2016, Java 8 - OpenJ9 66ms 64ms 55ms

Aside from being the fastest, I am of the opinion that the "Append Maybe" implementation shows the intent of the code the best. That is usually more important than the fraction of nanoseconds gained per iteration.

I left the benchmark code here in case anyone wanted to try it on their platform. Please contribute your results above if you do so!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatthewView Question on Stackoverflow
Solution 1 - JavaJon SkeetView Answer on Stackoverflow
Solution 2 - JavaStephen CView Answer on Stackoverflow
Solution 3 - JavabragboyView Answer on Stackoverflow
Solution 4 - JavaArtOfWarfareView Answer on Stackoverflow
Solution 5 - JavaReuel RibeiroView Answer on Stackoverflow
Solution 6 - JavaRohit Reddy KorrapoluView Answer on Stackoverflow
Solution 7 - JavaakashView Answer on Stackoverflow
Solution 8 - JavaRafiqView Answer on Stackoverflow
Solution 9 - JavaZakiView Answer on Stackoverflow
Solution 10 - JavaAntoineView Answer on Stackoverflow
Solution 11 - JavaJason DayView Answer on Stackoverflow
Solution 12 - JavaNickUnuchekView Answer on Stackoverflow
Solution 13 - JavaVikasdeep SinghView Answer on Stackoverflow
Solution 14 - JavaoguzhanView Answer on Stackoverflow
Solution 15 - JavaStephanView Answer on Stackoverflow
Solution 16 - JavaMohamed Farook Mohamed FazrinView Answer on Stackoverflow
Solution 17 - JavaegerardusView Answer on Stackoverflow