00:00

Difference Between StringBuffer and StringBuilder in Java

StringBuffer and StringBuilder are both mutable sequences of characters with the same methods (append, insert, delete, reverse…), the same internal growable array and the same capacity rules. The only real difference is synchronization:

  • StringBuffer (Java 1.0): every public method is synchronized, so one instance can be shared between threads.
  • StringBuilder (Java 5): no synchronization, so it is faster, and it is the right choice whenever the object is used by a single thread, which is nearly always.

1. Comparison Table

StringBuffer vs StringBuilder in Java
AspectStringBufferStringBuilder
SynchronizationAll public methods synchronizedNone
Thread safetySafe for individual callsNot thread-safe
PerformanceSlower (locking overhead)Faster
IntroducedJava 1.0Java 5
APIIdentical: both extend AbstractStringBuilder and implement CharSequence, Appendable, Comparable (11)
CapacityDefault 16; grows to (old × 2) + 2
equals()Not overridden in either: compares references
Recommended forA buffer shared by threads, legacy APIsEverything else

2. Identical API

SameApi.java
public class SameApi {
    public static void main(String[] args) {
        StringBuffer buffer = new StringBuffer("Java");
        StringBuilder builder = new StringBuilder("Java");

        // Identical API and results
        buffer.append(" 21").insert(0, "Hello ").reverse().reverse();
        builder.append(" 21").insert(0, "Hello ").reverse().reverse();
        System.out.println("StringBuffer  : " + buffer + " (capacity " + buffer.capacity() + ")");
        System.out.println("StringBuilder : " + builder + " (capacity " + builder.capacity() + ")");
        System.out.println("same text     : " + buffer.toString().equals(builder.toString()));
        System.out.println("common type   : " + (buffer instanceof CharSequence) + ", " + (builder instanceof CharSequence));
    }
}
Output
StringBuffer  : Hello Java 21 (capacity 20)
StringBuilder : Hello Java 21 (capacity 20)
same text     : true
common type   : true, true

Because the APIs are identical, switching from StringBuffer to StringBuilder is usually just a change of type name.

3. Thread Safety

Two threads append 20,000 characters each to one shared object:

SharedBetweenThreads.java
public class SharedBetweenThreads {
    static int run(Appendable target) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 20_000; i++) {
                try { target.append('x'); } catch (Exception e) { /* corrupted internal state */ }
            }
        };
        Thread a = new Thread(task), b = new Thread(task);
        a.start(); b.start(); a.join(); b.join();
        return ((CharSequence) target).length();
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("expected      : 40000");
        System.out.println("StringBuffer  : " + run(new StringBuffer()));
        System.out.println("StringBuilder : " + run(new StringBuilder()) + "  (often less when shared)");
    }
}
Output (sample run; the StringBuilder result varies)
expected      : 40000
StringBuffer  : 40000
StringBuilder : 20478  (often less when shared)

A shared StringBuilder loses characters or even throws exceptions. In real programs, though, you rarely share a string builder between threads: each thread usually builds its own text and then publishes the finished String, which needs no synchronization.

4. Performance

SpeedCompare.java
public class SpeedCompare {
    public static void main(String[] args) {
        int n = 5_000_000;
        for (int warm = 0; warm < 3; warm++) {           // let the JIT warm up
            new StringBuffer().append("x".repeat(10));
            new StringBuilder().append("x".repeat(10));
        }
        long t0 = System.nanoTime();
        StringBuffer sbf = new StringBuffer();
        for (int i = 0; i < n; i++) sbf.append('x');
        long t1 = System.nanoTime();
        StringBuilder sbd = new StringBuilder();
        for (int i = 0; i < n; i++) sbd.append('x');
        long t2 = System.nanoTime();
        System.out.printf("StringBuffer  : %3d ms%n", (t1 - t0) / 1_000_000);
        System.out.printf("StringBuilder : %3d ms%n", (t2 - t1) / 1_000_000);
    }
}
Output (sample run; timings vary)
StringBuffer  :  16 ms
StringBuilder :   7 ms

The JIT can sometimes remove uncontended locks, so the gap varies, but StringBuilder is never slower in single-threaded code.

5. Which Should You Use?

  • StringBuilder by default: local variables, methods, loops, toString() implementations.
  • StringBuffer only if a single instance is truly appended to by several threads, or an older API requires it.
  • String for the finished, immutable result. See String vs StringBuffer.

6. Summary

  • Both are mutable character sequences with the same API and capacity behaviour.
  • StringBuffer is synchronized and thread-safe per call; StringBuilder is not.
  • StringBuilder is faster and is the standard choice.
  • Neither overrides equals; compare contents explicitly.

Related Tutorials