Using character instead of String for single-character values in StringBuffer append
Solution 1
Appending a character as a char
will always be faster than appending it as a String
.
But does the performance difference matter? If you just do it once, it doesn't. If it is inside a cycle repeating its body a million times, then yes, it might matter.
If you already have the character at compile time, just append it as a character. If it is stored in a variable with String
type, don't bother accessing it e.g. with String.charAt(0)
or some other ways, simply just append the String
.
On a Side Note:
Favor the StringBuilder
class to StringBuffer
. StringBuilder
is faster because its methods are not synchronized (which you don't need in most cases).
On a Side Note #2:
This won't compile:
String text = new StringBuffer().append("some string").append('c');
append()
returns StringBuffer
for chaining. You need to call toString()
on it:
String text = new StringBuffer().append("some string").append('c').toString();
Solution 2
Out of curiosity I ran a micro benchmark with jmh (including GC monitoring). Using a String is marginally slower but the difference is minimal: around 5 ns (nanoseconds) per invocation and no significant difference on GC activity.
If you called append("c")
instead of append('c')
one million times, it would add 5 ms to your program.
Benchmark results, including gc time - n
represents the initial length of the StringBuilder:
Benchmark (n) Mode Cnt Score Error Units
SO28344.appendChar 0 avgt 30 16.476 ± 0.331 ns/op
SO28343294.appendChar:·gc.time 0 avgt 30 256.000 ms
SO28343294.appendString 0 avgt 30 22.048 ± 0.345 ns/op
SO28343294.appendString:·gc.time 0 avgt 30 220.000 ms
SO28343294.appendChar 50 avgt 30 17.323 ± 0.967 ns/op
SO28343294.appendChar:·gc.time 50 avgt 30 67.000 ms
SO28343294.appendString 50 avgt 30 20.944 ± 1.466 ns/op
SO28343294.appendString:·gc.time 50 avgt 30 74.000 ms
SO28343294.appendChar 1000 avgt 30 58.396 ± 0.811 ns/op
SO28343294.appendChar:·gc.time 1000 avgt 30 25.000 ms
SO28343294.appendString 1000 avgt 30 64.572 ± 4.779 ns/op
SO28343294.appendString:·gc.time 1000 avgt 30 24.000 ms
Code:
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO28343294 {
@Param({"0", "50", "1000"}) int n;
Random r = new Random();
StringBuilder sb;
String s;
char c;
@Setup(Level.Invocation) public void populate() {
sb = new StringBuilder(n + 5);
for (int i = 0; i < n; i++) {
sb.append((char) (r.nextInt(26) + 'a'));
}
c = (char) (r.nextInt(26) + 'a');
s = new String(new char[] { c });
}
@Benchmark public StringBuilder appendString() {
return sb.append(s);
}
@Benchmark public StringBuilder appendChar() {
return sb.append(c);
}
}
Solution 3
See the implementation of each and compare them:
public AbstractStringBuilder append(char c)
:
public AbstractStringBuilder append(char c) {
int newCount = count + 1;
if (newCount > value.length)
expandCapacity(newCount);
value[count++] = c;
return this;
}
public AbstractStringBuilder append(String str)
:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;
}
Which one do you prefer when you have the option to use both?
If I have 1000s of lines, I will really prefer to use append(char c)
for better performances, but for one line, it doesn't really matter.
![Zeeshan](https://i.stack.imgur.com/LA2zu.jpg?s=256&g=1)
Zeeshan
Updated on June 04, 2022Comments
-
Zeeshan about 2 years
I was going through the PMD rule
AppendCharacterWithChar
. It says Avoid concatenating characters as strings in StringBuffer.append.StringBuffer sb = new StringBuffer(); // Avoid this sb.append("a"); // use instead something like this StringBuffer sb = new StringBuffer(); sb.append('a');
Do I really need this PMD rule? Is there much performance difference between the following two piece of code?
String text = new StringBuffer().append("some string").append('c').toString(); String text = new StringBuffer().append("some string").append("c").toString();
-
icza over 9 years
sb.append("a")
doesn't create new strings. String literals are interened. -
Keval over 9 yearsbut what if there is no string object with value 'a' ? will that create a new object in string pool ?
-
icza over 9 yearsYes, if it is not yet in the pool, it will be added to it. But since the
String
literal is part of the class definition, it might be added to theString
pool when the class is loaded (implementation dependent). -
avalancha over 6 yearsAs usual with these questions it goes: yeah, maybe, could be a performance gain. Make a benchmark on your system, get some proof, then decide how much effort it is worth putting in for your situation
-
pramodc84 over 5 yearsAbove one will violate another PMD rule pmd.sourceforge.io/pmd-4.3/rules/strings.html. AvoidStringBufferField says StringBuffers can grow quite a lot, and so may become a source of memory leak (if the owning class has a long life time).