Attic
CollectionsStrings

StringBuilder & StringBuffer

Mutable character buffers, capacity expansion, loop concatenation pitfalls, and thread-safety differences

Because Java String instances are immutable, performing repeated string modifications or concatenations using the + operator generates redundant intermediate String objects on the heap. StringBuilder and StringBuffer provide resizable, mutable character buffers to assemble text with optimal performance.


The Loop Concatenation Anti-Pattern

A common performance bug in Java is concatenating strings inside a loop using +:

StringConcatTrap.java
// ANTI-PATTERN: O(n^2) Time and Space Overhead
String result = "";
for (int i = 0; i < 10000; i++) {
    result += i; // Allocates a brand-new String object and copies all characters on every iteration!
}

Why + in loops destroys performance: Each += copies the entire accumulated character buffer into a new heap object. For n iterations, this allocates (1 + 2 + ... + n) = O(n^2) total characters, triggering heavy Garbage Collection pauses.

The Idiomatic StringBuilder Solution

Using StringBuilder reduces the time complexity from O(n^2) to O(n):

StringBuilderSolution.java
// OPTIMAL: O(n) Linear Time
StringBuilder sb = new StringBuilder(10000);
for (int i = 0; i < 10000; i++) {
    sb.append(i);
}
String result = sb.toString();

StringBuilder Core API

StringBuilder methods mutate the internal buffer in-place and return this, allowing fluent method chaining:

MethodParametersReturn TypeDescription / Behavior
append(T val)primitive / ObjectStringBuilderAppends text representation to the end of the buffer
insert(int offset, T val)int, primitive / ObjectStringBuilderInserts text at index offset, shifting following characters right
delete(int start, int end)int, intStringBuilderDeletes characters in half-open range [start, end)
deleteCharAt(int index)intStringBuilderRemoves character at specified 0-based index
reverse()—StringBuilderReverses buffer characters in-place
setCharAt(int index, char ch)int, charvoidOverwrites character at index in O(1) time
length()—intCurrent number of characters stored in buffer
capacity()—intTotal character slots currently allocated
ensureCapacity(int min)intvoidExpands buffer capacity if below min
setLength(int newLength)intvoidTruncates or extends buffer (pads with NUL chars)
toString()—StringCreates an immutable String from the buffer
StringBuilderDemo.java
StringBuilder sb = new StringBuilder("Hello");

sb.append(" World");                  // "Hello World"
sb.insert(5, ",");                    // "Hello, World"
sb.delete(5, 6);                      // "Hello World"
sb.setCharAt(0, 'h');                 // "hello World"
sb.reverse();                         // "dlroW olleh"

String output = sb.toString();

Capacity Allocation & Geometric Resizing

Under the hood, StringBuilder is backed by a mutable byte[] buffer.

  1. Default Capacity: If instantiated without arguments (new StringBuilder()), the default initial capacity is 16 characters.
  2. Explicit Capacity: If the final length is roughly known, pass the expected capacity to avoid resizing:
    StringBuilder sb = new StringBuilder(64); // pre-allocates 64 slots
  3. Growth Mechanics: When characters exceed current capacity, StringBuilder grows geometrically:
    newCapacity = (oldCapacity * 2) + 2
    Geometric growth guarantees an amortized O(1) cost per append() operation.

StringBuilder vs StringBuffer

DimensionStringBuilderStringBuffer
Thread SafetyNot thread-safe (unsynchronized)Thread-safe (synchronized methods)
Throughput / SpeedFastest (Zero lock acquisition overhead)Slower due to synchronization monitor locks
JDK IntroductionJava 5+Java 1.0+ (Legacy)
RecommendationDefault choice for single-threaded code, algorithms, and local variablesLegacy applications with shared multi-threaded logging buffers

Why StringBuffer is Rarely Needed Today: In modern Java architectures, string construction is almost exclusively local to a single thread or method stack. Using StringBuffer imposes unnecessary synchronization overhead.


Simple String Concatenation Outside Loops

For single-line concatenation statements like:

String fullName = firstName + " " + lastName;

You do not need to manually construct a StringBuilder.

Since Java 9, the compiler replaces simple string concatenation with invokedynamic calls backed by java.lang.invoke.StringConcatFactory. The JVM dynamically crafts the most optimal strategy at runtime without creating redundant intermediate objects.

On this page