Skip to content
Java strings 6 min read

StringBuilder

When you need to build or modify a string in a loop or across multiple steps, StringBuilder is your go-to tool. It gives you a mutable, resizable character buffer without the allocation overhead of repeatedly creating new String objects — and without the synchronization cost of StringBuffer.

Why StringBuilder Exists

Java strings are immutable. Every time you concatenate or modify a String, you get a brand-new object:

String result = "";
for (int i = 0; i < 5; i++) {
    result += i;   // creates a new String object on every iteration
}
System.out.println(result);

Output:

01234

That loop silently allocates five intermediate String objects that are immediately discarded. At small scale you won’t notice, but in a loop with thousands of iterations those short-lived objects pile up and strain the garbage collector.

StringBuilder solves this by keeping a single, resizable char buffer that you mutate in place:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
    sb.append(i);  // mutates the same object — zero extra allocations
}
System.out.println(sb.toString());

Output:

01234

Note: The Java compiler already converts simple + concatenations of literals at compile time. But for concatenation inside a loop, modern compilers (Java 9+) may still produce suboptimal bytecode. Explicitly using StringBuilder keeps your intent clear and guarantees efficiency.

Creating a StringBuilder

// 1. Empty buffer — default capacity is 16 characters
StringBuilder sb1 = new StringBuilder();

// 2. Pre-populated — capacity = string.length() + 16
StringBuilder sb2 = new StringBuilder("Hello");

// 3. Specific initial capacity — avoids early resizing
StringBuilder sb3 = new StringBuilder(256);

Tip: If you know the rough final length, pass it as the initial capacity. Skipping unnecessary resize-and-copy cycles gives a measurable speed boost for large strings.

Commonly Used Methods

append()

The most-used method. It adds content to the end of the buffer and returns this, so you can chain calls:

StringBuilder sb = new StringBuilder();
sb.append("Name: ")
  .append("Alice")
  .append(", Age: ")
  .append(30);
System.out.println(sb.toString());

Output:

Name: Alice, Age: 30

append() is overloaded for String, int, long, float, double, char, boolean, char[], CharSequence, and Object.

insert()

Inserts content at any position in the buffer:

StringBuilder sb = new StringBuilder("Hello World");
sb.insert(5, ",");
System.out.println(sb);

Output:

Hello, World

replace()

Replaces the characters in the half-open range [start, end):

StringBuilder sb = new StringBuilder("Hello Java");
sb.replace(6, 10, "World");
System.out.println(sb);

Output:

Hello World

delete() and deleteCharAt()

StringBuilder sb = new StringBuilder("Hello, World!");
sb.delete(5, 7);          // removes ", "
System.out.println(sb);

sb.deleteCharAt(5);       // removes the space
System.out.println(sb);

Output:

Hello World!
HelloWorld!

reverse()

Reverses the entire sequence in place — perfect for the classic reverse a string problem:

StringBuilder sb = new StringBuilder("abcde");
sb.reverse();
System.out.println(sb);

Output:

edcba

indexOf() and lastIndexOf()

StringBuilder sb = new StringBuilder("banana");
System.out.println(sb.indexOf("an"));      // 1
System.out.println(sb.lastIndexOf("an"));  // 3

substring()

Returns a new String extracted from the buffer — the buffer itself is unchanged:

StringBuilder sb = new StringBuilder("Hello, World!");
System.out.println(sb.substring(7));       // World!
System.out.println(sb.substring(7, 12));   // World

length() and capacity()

StringBuilder sb = new StringBuilder("Hello");
System.out.println(sb.length());    // 5  — chars currently stored
System.out.println(sb.capacity());  // 21 — internal array size (5 + 16)

charAt() and setCharAt()

StringBuilder sb = new StringBuilder("Hello");
System.out.println(sb.charAt(1));    // e
sb.setCharAt(0, 'h');
System.out.println(sb);              // hello

Practical Example: Building a Query String

public class QueryBuilder {
    public static String build(String base, String[][] params) {
        StringBuilder sb = new StringBuilder(base);
        sb.append('?');
        for (int i = 0; i < params.length; i++) {
            sb.append(params[i][0])
              .append('=')
              .append(params[i][1]);
            if (i < params.length - 1) {
                sb.append('&');
            }
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String[][] params = {{"page", "1"}, {"sort", "asc"}, {"q", "java"}};
        System.out.println(build("https://example.com/search", params));
    }
}

Output:

https://example.com/search?page=1&sort=asc&q=java

Quick Reference: Method Table

MethodDescription
append(x)Appends x to the end
insert(offset, x)Inserts x at offset
replace(start, end, str)Replaces [start, end) with str
delete(start, end)Removes chars at [start, end)
deleteCharAt(index)Removes single char at index
reverse()Reverses the sequence
indexOf(str)First occurrence of str
lastIndexOf(str)Last occurrence of str
substring(start)String from start to end
substring(start, end)String from start to end
charAt(index)Char at index
setCharAt(index, ch)Sets char at index to ch
length()Current character count
capacity()Current internal array size
ensureCapacity(min)Guarantees at least min capacity
trimToSize()Shrinks buffer to fit current length
toString()Returns a String snapshot

StringBuilder vs StringBuffer

Both classes share almost the same API and even the same parent class (AbstractStringBuilder). The one key difference is thread safety:

FeatureStringBuilderStringBuffer
Thread-safeNoYes (synchronized)
PerformanceFasterSlightly slower
Introduced inJava 5Java 1.0
Use caseSingle-threaded codeShared across threads

Tip: In everyday single-threaded code — which covers the vast majority of string-building tasks — always prefer StringBuilder. Only switch to StringBuffer when multiple threads genuinely share the same buffer instance.

Under the Hood

StringBuilder and StringBuffer both extend AbstractStringBuilder, which holds the actual storage. In Java 8 and earlier that storage is a char[]. From Java 9 onwards (with the Compact Strings optimisation, JEP 254), the JVM stores Latin-1 strings as a byte[] with one byte per character instead of two, halving memory for ASCII-only content. A coder byte tracks whether the buffer is Latin-1 or UTF-16.

Capacity growth: When an append would overflow the current array, AbstractStringBuilder allocates a new array of size (oldCapacity × 2) + 2 and copies the existing data across. This doubling strategy gives O(1) amortised append performance — the same idea as ArrayList growth. You pay for a copy occasionally, but the average cost per character stays constant.

Because StringBuilder methods are not synchronized, the JIT compiler is free to inline and optimise them aggressively. In tight loops the difference versus StringBuffer can be significant. If you look at the generated bytecode with the javap tool, you’ll see that method calls on StringBuilder are often inlined completely away.

Warning: StringBuilder is not thread-safe. If two threads call append() concurrently on the same instance, the internal count field and value array can become inconsistent, producing garbled output or an ArrayIndexOutOfBoundsException. Use StringBuffer or a ThreadLocal<StringBuilder> if you need shared mutable string building.

Converting Between Types

// StringBuilder -> String
StringBuilder sb = new StringBuilder("Hello");
String s = sb.toString();

// String -> StringBuilder
String original = "World";
StringBuilder sb2 = new StringBuilder(original);

// StringBuilder -> StringBuffer (rare, but possible)
StringBuffer sbf = new StringBuffer(sb.toString());

Note: toString() creates a new String object that shares the underlying byte[] (or char[]) with the builder until either object is modified — a copy-on-write optimisation in some JVM implementations. This makes the conversion essentially free in the common case.

Last updated June 13, 2026
Was this helpful?