If you’re working with Spring, check out "REST With Spring":

>> CHECK OUT THE COURSE

1. Overview

In this short article, we’re going to look at similarities and differences between StringBuilder and StringBuffer in Java.

Simply put, StringBuilder was introduced in Java 1.5 as a replacement for StringBuffer.

2. Similarities

Both StringBuilder and StringBuffer create objects that hold a mutable sequence of characters. Let’s see how this works, and how it compares to an immutable String class:

String immutable = "abc";
immutable = immutable + "def";

Even though it may look like that we’re modifying the same object by appending “def”, we are creating a new one because String instances can’t be modified.

When using either StringBuffer or StringBuilder, we can use the append() method:

StringBuffer sb = new StringBuffer("abc");
sb.append("def");

In this case, there was no new object created. We have called the append() method on sb instance and modified its content. StringBuffer and StringBuilder are mutable objects.

3. Differences

StringBuffer is synchronized and therefore thread-safe. StringBuilder is compatible with StringBuffer API but with no guarantee of synchronization.

Because it’s not a thread-safe implementation, it is faster and it is recommended to use it in places where there’s no need for thread safety.

3.1. Performance

In small iterations, the performance difference is insignificant. Let’s do a quick micro-benchmark with JMH:

@State(Scope.Benchmark)
public static class MyState {
    int iterations = 1000;
    String initial = "abc";
    String suffix = "def";
}

@Benchmark
public StringBuffer benchmarkStringBuffer(MyState state) {
    StringBuffer stringBuffer = new StringBuffer(state.initial);
    for (int i = 0; i < state.iterations; i++) {
        stringBuffer.append(state.suffix);
    }
    return stringBuffer;
}

@Benchmark
public StringBuilder benchmarkStringBuilder(MyState state) {
    StringBuilder stringBuilder = new StringBuilder(state.initial);
    for (int i = 0; i < state.iterations; i++) {
        stringBuilder.append(state.suffix);
    }
    return stringBuilder;
}

We have used the default Throughput mode – i.e. operations per unit of time (higher score is better), which gives:

Benchmark                                          Mode  Cnt      Score      Error  Units
StringBufferStringBuilder.benchmarkStringBuffer   thrpt  200  86169.834 ±  972.477  ops/s
StringBufferStringBuilder.benchmarkStringBuilder  thrpt  200  91076.952 ± 2818.028  ops/s

If we increase the number of iterations from 1k to 1m then we get:

Benchmark                                          Mode  Cnt   Score   Error  Units
StringBufferStringBuilder.benchmarkStringBuffer   thrpt  200  77.178 ± 0.898  ops/s
StringBufferStringBuilder.benchmarkStringBuilder  thrpt  200  85.769 ± 1.966  ops/s

However, let’s bear in mind that this is a micro-benchmark, which may or may not have a real impact on the actual, real-world performance of an application.

4. Conclusions

Simply put, the StringBuffer is a thread-safe implementation and therefore slower than the StringBuilder.

In single-threaded programs, we can take of the StringBuilder. Yet, the performance gain of StringBuilder over StringBuffer may be too small to justify replacing it everywhere. It’s always a good idea to profile the application and understand its runtime performance characteristics before doing any kind of work to replace one implementation with another.

Finally, as always, the code used during the discussion can be found over on GitHub.

The new Certification Class of "REST With Spring" is finally out:

>> CHECK OUT THE COURSE