00:00

StringBuffer Class in Java – Mutable, Thread-Safe Strings

StringBuffer is a mutable sequence of characters in java.lang. Unlike String, its contents can be changed in place with methods such as append, insert and delete, without creating a new object each time. All of its public methods are synchronized, so a single StringBuffer can be safely shared between threads.

Since Java 5, the unsynchronized StringBuilder is preferred in most code. This tutorial covers StringBuffer's constructors, methods and capacity management, and explains when its thread safety actually matters.

1. Constructors

ConstructorInitial capacity
StringBuffer()16 characters
StringBuffer(int capacity)The given capacity
StringBuffer(String str)16 + str.length(), initialised with str
StringBuffer(CharSequence seq)16 + seq.length()

2. Common Methods

StringBufferMethods.java
public class StringBufferMethods {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Java");
        sb.append(" Tutorial");                 System.out.println("append      : " + sb);
        sb.insert(5, "Core ");                  System.out.println("insert      : " + sb);
        sb.replace(5, 9, "Advanced");           System.out.println("replace     : " + sb);
        sb.delete(4, 13);                       System.out.println("delete      : " + sb);
        sb.deleteCharAt(sb.length() - 1);       System.out.println("deleteCharAt: " + sb);
        sb.setCharAt(0, 'j');                   System.out.println("setCharAt   : " + sb);
        sb.reverse();                           System.out.println("reverse     : " + sb);
        sb.reverse();
        System.out.println("indexOf(\"Tut\"): " + sb.indexOf("Tut"));
        System.out.println("charAt(1)     : " + sb.charAt(1));
        System.out.println("length        : " + sb.length());
        // Methods return the same object, so calls can be chained
        System.out.println("chained       : " + new StringBuffer().append("A").append(1).append(true).append(2.5));
    }
}
Output
append      : Java Tutorial
insert      : Java Core Tutorial
replace     : Java Advanced Tutorial
delete      : Java Tutorial
deleteCharAt: Java Tutoria
setCharAt   : java Tutoria
reverse     : airotuT avaj
indexOf("Tut"): 5
charAt(1)     : a
length        : 12
chained       : A1true2.5
MethodPurpose
append(x)Add any type (String, int, char, boolean, Object…) at the end
insert(index, x)Insert at a position
replace(start, end, str)Replace characters from start (inclusive) to end (exclusive)
delete(start, end) / deleteCharAt(i)Remove a range or one character
reverse()Reverse the contents in place
setCharAt(i, c) / charAt(i)Write or read a character
indexOf / lastIndexOfSearch for a substring
substringCopy part as a new String
toString()Produce an immutable String

Most modifying methods return this, which allows method chaining.

3. Length vs Capacity

Length is the number of characters stored; capacity is the size of the internal array. When the content outgrows the capacity, the buffer allocates a larger array (old capacity × 2 + 2) and copies the characters:

CapacityDemo.java
public class CapacityDemo {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();           // default capacity 16
        System.out.println("empty:        length=" + sb.length() + ", capacity=" + sb.capacity());
        sb.append("1234567890123456");                  // exactly 16 chars
        System.out.println("16 chars:     length=" + sb.length() + ", capacity=" + sb.capacity());
        sb.append("x");                                 // grows: (16 * 2) + 2 = 34
        System.out.println("17 chars:     length=" + sb.length() + ", capacity=" + sb.capacity());

        StringBuffer sized = new StringBuffer(100);     // pre-size when the final length is known
        System.out.println("pre-sized:    capacity=" + sized.capacity());
        StringBuffer fromText = new StringBuffer("Java"); // capacity = 16 + 4
        System.out.println("from \"Java\":  capacity=" + fromText.capacity());

        sb.setLength(5);                                 // truncate
        sb.trimToSize();                                 // shrink capacity to length
        System.out.println("after setLength(5) + trimToSize(): " + sb + ", capacity=" + sb.capacity());
    }
}
Output
empty:        length=0, capacity=16
16 chars:     length=16, capacity=16
17 chars:     length=17, capacity=34
pre-sized:    capacity=100
from "Java":  capacity=20
after setLength(5) + trimToSize(): 12345, capacity=5

If you know roughly how long the result will be, pass that capacity to the constructor or call ensureCapacity() to avoid repeated resizing.

4. Thread Safety

Three threads append 10,000 characters each to a shared StringBuffer and a shared StringBuilder:

ThreadSafeBuffer.java
public class ThreadSafeBuffer {
    public static void main(String[] args) throws InterruptedException {
        StringBuffer buffer = new StringBuffer();       // synchronized methods
        StringBuilder builder = new StringBuilder();    // not synchronized

        Runnable task = () -> {
            for (int i = 0; i < 10_000; i++) {
                buffer.append('a');
                try { builder.append('a'); } catch (ArrayIndexOutOfBoundsException e) { /* corrupted state */ }
            }
        };
        Thread t1 = new Thread(task), t2 = new Thread(task), t3 = new Thread(task);
        t1.start(); t2.start(); t3.start();
        t1.join();  t2.join();  t3.join();

        System.out.println("Expected length:        30000");
        System.out.println("StringBuffer length:    " + buffer.length());
        System.out.println("StringBuilder length:   " + builder.length() + "  (often less: lost updates)");
    }
}
Output (sample run; the StringBuilder result varies)
Expected length:        30000
StringBuffer length:    30000
StringBuilder length:   29763  (often less: lost updates)

Each StringBuffer method call is atomic. Note, however, that a sequence of calls, such as checking length() and then appending, is not atomic; you would still need external synchronization for that. In practice, sharing a mutable string builder between threads is rare, which is why StringBuilder is the usual choice.

5. String vs StringBuffer vs StringBuilder

AspectStringStringBufferStringBuilder
MutabilityImmutableMutableMutable
Thread safetyYes (immutable)Yes (synchronized)No
Performance for many modificationsSlow (new objects)FastFastest
SinceJava 1.0Java 1.0Java 5
equals() compares contentYesNo (identity); use compareTo or toString()No (identity)

6. Summary

  • StringBuffer is a mutable, synchronized character sequence.
  • Use append, insert, replace, delete and reverse to modify it in place, then toString().
  • Default capacity is 16 and grows to (old × 2 + 2) when needed.
  • Prefer StringBuilder unless the same instance is shared between threads.

Related Tutorials