StringBuilder builds text through mutable operations such as append, insert and delete. StringBuffer offers similar operations with synchronized methods. Both are different from immutable String values, and neither choice removes the need to understand sharing, indices and the final conversion to text.
Review the related foundation lesson when you need a prerequisite. The complete programs below use Java 17-compatible syntax.
Mutable text construction
StringBuilder stores a mutable sequence of characters. append adds content without creating a separate immutable String for every intermediate concatenation.
Use a builder when a sequence of operations gradually assembles text, particularly inside a loop. append updates the same builder and usually returns that builder, allowing chained calls. toString creates the String result for consumers that expect immutable text. For a short ordinary concatenation expression, the + operator is often clearer; do not replace every readable string expression with a builder automatically.
Editing and snapshots
insert, delete and reverse modify the builder. toString produces an immutable String snapshot that can be returned or displayed.
length counts the text currently stored, while capacity describes available internal storage before expansion is needed. Increasing capacity does not add visible characters. setLength changes the visible length and can append null characters when length is increased, so it is not equivalent to ensuring capacity. A reasonable initial capacity may reduce reallocations when a large result size is known.
Synchronization differences
StringBuffer provides synchronized methods, while StringBuilder does not. Synchronizing individual methods does not automatically make a multi-step application operation atomic.
StringBuilder is not synchronized and is normally appropriate for a builder local to one operation. StringBuffer synchronizes individual methods, but a sequence such as checking length and then appending is not automatically one atomic action. Shared multi-step operations may still require external coordination. Avoid sharing mutable text accumulators unnecessarily; a final String is often a simpler value to pass between components.
Choosing a builder
Use a builder for repeated construction, especially inside a loop. Simple one-expression concatenation is readable and is often optimized by the compiler.
insert uses an insertion offset, while delete and replace use an inclusive start and exclusive end. After an insertion, later positions may shift. Write index-sensitive edits in an order that still refers to the intended text. Calling reverse changes the builder's current sequence, and reusing the builder later continues from that changed state unless you explicitly reset or replace it.
Running the examples
Save name.java, compile it with javac --release 17 name.java, and run java name. Use each program's own public class name and filename.
How ChecklistBuilder produces its output
The report begins with Checks: followed by a space, a prefix whose length is eight. The first iteration appends brakes without a separator because the length is not yet greater than eight. Later iterations see a longer builder and add comma-space before lights and bell. toString returns the final display, Checks: brakes, lights, bell.
How BuilderAppend produces its output
The first append chain changes the builder to Order 12. toString records that text as an immutable String. The later append changes only the builder's current contents, so text still prints Order 12 while printing the builder shows the additional word. The snapshot does not follow later mutations.
How BuilderEdits produces its output
Inserting at zero places wild and a space before cat. The resulting space occupies index four. delete(4, 5) removes that one character because the upper bound is exclusive. The final text is wildcat. Choosing indices based on the original three-character string would instead describe the wrong positions after insertion.
Reset a builder explicitly when reusing it
setLength(0) clears a builder's current text without replacing the builder variable. This can be useful when formatting several independent rows, but clearing is easy to forget and can accidentally concatenate one row with the next. A fresh local builder per independent result is often clearer. If you reuse one, publish each toString snapshot before clearing it and do not expose the mutable builder as if it were stable text. For practice, build two labels in sequence using one builder, clearing between them. Verify that the first saved String remains unchanged after the second label is assembled and after the builder is cleared again.
Compare built text as text
StringBuilder does not override Object.equals to compare its character contents. Two different builders containing the same letters therefore do not become content-equal merely because their displays match. If the requirement is text equality, compare their resulting Strings or use an appropriate character-content operation. This reinforces the boundary between a mutable construction tool and the immutable value it produces. Avoid using a changing builder as a key whose meaning depends on its current display. A report's stored identifier should normally be a String value, while its builder should remain local to formatting. Publishing the result instead of the accumulator makes that distinction visible to callers.
Java Summary
- StringBuilder stores a mutable sequence of characters. append adds content without creating a separate immutable String for every intermediate concatenation.
- insert, delete and reverse modify the builder. toString produces an immutable String snapshot that can be returned or displayed.
- StringBuffer provides synchronized methods, while StringBuilder does not. Synchronizing individual methods does not automatically make a multi-step application operation atomic.
- Use a builder for repeated construction, especially inside a loop. Simple one-expression concatenation is readable and is often optimized by the compiler.
- Continue with Java exercises, Java quizzes and the Java study roadmap.
Java Examples
Each example shows the code, the output it produces and what each part does.
Java StringBuilder and StringBuffer in a complete program
public class ChecklistBuilder {
public static void main(String[] args) {
StringBuilder report = new StringBuilder("Checks: ");
for (String item : new String[] {
"brakes", "lights", "bell"
}) {
if (report.length() > 8) report.append(", ");
report.append(item);
}
System.out.println(report.toString());
}
}Checks: brakes, lights, bellLine-by-line explanation
- Line 10Evaluates
report.toString()and printsChecks: brakes, lights, bell.
Append mutates one builder
public class BuilderAppend {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("Order");
builder.append(' ').append(12);
String text = builder.toString();
builder.append(" ready");
System.out.println(text);
System.out.println(builder);
}
}Order 12
Order 12 readyLine-by-line explanation
- Line 7Evaluates
textand printsOrder 12. - Line 8Evaluates
builderand printsOrder 12 ready.
Insertion and deletion shift text
public class BuilderEdits {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("cat");
builder.insert(0, "wild ");
builder.delete(4, 5);
System.out.println(builder);
}
}wildcatLine-by-line explanation
- Line 6Evaluates
builderand printswildcat.
Java FAQ
Does ensureCapacity add spaces?
No. It reserves internal storage without changing the visible length or characters. To add spaces, append them explicitly. setLength has different behavior and can introduce null characters when it expands the visible sequence.
Is StringBuffer always enough for thread safety?
No. Its individual synchronized operations do not automatically combine several calls into one atomic transaction. If a shared sequence of checks and updates must be indivisible, coordinate that whole sequence or avoid sharing the buffer.
How do I remove the last character from a StringBuilder?
Call sb.setLength(sb.length() - 1) or sb.deleteCharAt(sb.length() - 1), after checking that the builder isn't empty. This is a common way to remove a trailing comma after appending items in a loop, although String.join or StringJoiner avoids the problem entirely.
What is the initial capacity of a StringBuilder?
An empty new StringBuilder() starts with room for 16 characters and grows automatically when needed. If you know the final size, new StringBuilder(5000) avoids repeated growth, but for ordinary programs the default is fine.
Does Java use StringBuilder for + automatically?
Since Java 9, the compiler turns a single concatenation expression into an efficient call that builds the result in one step. That only applies within one expression; text += piece inside a loop still creates a new String on every pass, so an explicit StringBuilder remains the right choice there.
Java Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
What does toString return from a builder?
Which range does delete(2, 5) remove?
Java Quiz
2 questions to check what you learned.
What does capacity measure?
- Available storage before expansion is needed
- The number of currently visible characters only
- The maximum permitted lifetime length
Which class is normally preferable for a local unshared builder?
- StringBuilder
- StringBuffer because every call must synchronize
- String because append mutates it