[java] Remove last character of a StringBuilder?

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

This question is related to java stringbuilder

The answer is


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

since you know the character you want to remove you can use this

sb.TrimEnd(",");

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"

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

I personally like to append a backspace character (or more for longer "delimiter") at the end:

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

sb.append('\b');

Note that it has problems:

  • how \b is displayed depends on the environment,
  • the length() of the String content might be different from the length of "visibile" characters

When \b looks OK and the length doesn't matter such as logging to a console, this seems good enough to me.


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

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


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

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.


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)


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

Alternatively,

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

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


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

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

Another alternative

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

Here is another solution:

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

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