As you already know, java's StringBuilder and StringBuffer are virtually the same and the only difference is that all methods on the Buffer are synchronized. And the conventional wisdom tells us that synchronization adds extra and unnecessary overhead. There are articles about how it is best to use Builder in the general case of the local string concatenation. Let's test it:
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@Warmup(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
public class MyBenchmark {
@Benchmark
public String testBuilder() {
StringBuilder b = new StringBuilder(1024);
b.append("Hello");
b.append(' ');
b.append(3);
b.append("am!");
return b.toString();
}
@Benchmark
public String testBuffer() {
StringBuffer b = new StringBuffer(1024);
b.append("Hello");
b.append(' ');
b.append(3);
b.append("am!");
return b.toString();
}
}
And the result on JDK 1.7:
Benchmark Mode Samples Score Error Units
o.s.t.MyBenchmark.testBuffer avgt 5 324.303 ± 94.488 ns/op
o.s.t.MyBenchmark.testBuilder avgt 5 290.062 ± 96.538 ns/op
Looks like the Builder is faster, does not it? Hold on, take a look at the confidence interval. And why are we using 1.7, let's try JDK 1.8:
Benchmark Mode Samples Score Error Units
o.s.t.MyBenchmark.testBuffer avgt 5 287.292 ± 49.302 ns/op
o.s.t.MyBenchmark.testBuilder avgt 5 280.481 ± 41.504 ns/op
And, just for fun, JDK 1.9 (dev build):
Benchmark Mode Samples Score Error Units
o.s.t.MyBenchmark.testBuffer avgt 5 273.393 ± 65.169 ns/op
o.s.t.MyBenchmark.testBuilder avgt 5 269.278 ± 27.776 ns/op
Java has a JIT compiler that can eliminate extra synchronization from the bytecode. And the case when synchronization is on a local instance that never leaves the thread is one of the easiest. JIT was getting better and better as the time passed, and by now is a truly great piece of software.
Unless you are stuck on an old JVM, there is not much difference in StringBuilder and StringBuffer performance. At least if all you add to the builder is other strings, chars and integers. Curious why? Because OpenJDK has intrinsics for those methods only:
_StringBuffer_append_char
_StringBuffer_append_int
_StringBuffer_append_String
Of course that may change in the future.