17

In many cases, we need to delete the last char of a StringBuilder/StringBuffer. For example, given a int[]{1,2,3}, to implement a String toString(int[] a) method, contacting each elements with a comma separator. The output should be 1,2,3, no tailing comma.

We can easily write a loop:

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 ','

but always we need to remove the tailing ','. There are two ways to implement it:

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

and

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

Which one is recommended? Why?

NOTE: I know what does Arrays.toString do. That's just an example to describe my question, maybe not quite proper. This is not a discussion about strings concatenation but the best practices of StringBuffer/StringBuilder.

Lolo
  • 4,277
  • 2
  • 25
  • 24
Weibo Li
  • 3,565
  • 3
  • 24
  • 36

4 Answers4

17

Actually, there is very little in it and is probably dependent on hardware and other factors.

The setLength() method simply alters the count and overwrites the unwanted value in the array with a zero byte.

The deleteCharAt() performs an array copy internally, before altering the count. That sounds dramatic, but the array being copied is actually zero-length because you're removing the last character.

I would recommend going for setLength() as it is shorter to type and I think makes it clearer what you are doing. If performance is an issue and, on measuring, you find this is the bottleneck for you, then perhaps you could consider a different algorithm that doesn't require changing the size (as per JB Nizet's answer).

ᴇʟᴇvᴀтᴇ
  • 12,285
  • 4
  • 43
  • 66
2

How you do it properly is to conditionally prepend the comma:

for (int i = 0; i < nums.length; i++) {
    if (i > 0)
        sb.append(',');
    sb.append(nums[i]);
}

Then you don't need to worry about removing the last character, because it's already correct.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

I wouldn't do it like this. Instead, I would only add a trailing comma if the element is not the last element of the array. Or I would use Guava's Joiner (or Apache-commons StringUtils), which makes it much clearer:

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

NB: I just noticed that Guava's Joiner doesn't deal with primitive arrays. You should get the idea from the above anyway.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thank you for your answer. But this is not a discussion about contacting strings but the best practices of StringBuffer/StringBuilder. – Weibo Li Feb 08 '14 at 11:50
  • 1
    The best practice, to me, is to avoid appending things that you know you'll have to remove after. Maybe you should come up with a use case where it's really necessary. I can't remember having had to do that in years of programming in Java. – JB Nizet Feb 08 '14 at 12:20
0

If you need even greater speed, your use case might allow you to just keep your own variable

int activelength;

Then you can just do this operation with

activelength--;

This would be faster than

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

which takes extra time clearing out the byte or word for safety.

Of course, the rest of your context will determine if this is a win for you or not, because other operations you may be needing, such as growing the StringBuilder, would take some extra logic to detect and invoke.

Mark Goldfain
  • 731
  • 2
  • 8
  • 24