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();
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();
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With