StringBuilder vs StringBuffer
What is inside StringBuilder and StringBuffer, how the byte[] buffer grows, what synchronized really costs, why + in a loop is still O(n²) after JEP 280, and when to use String.join or StringJoiner.
On this page
🧱 What are StringBuilder and StringBuffer?
They are mutable sequences of characters: you append, insert and delete in place, then call toString() once to get an immutable String. Building a 10,000-line CSV export with StringBuilder touches one growing buffer; building it with s += line creates 10,000 throwaway String objects. StringBuffer is the Java 1.0 version with every method synchronized; StringBuilder (Java 5) is the same API without the locks.
For this lesson, we'll focus on what's inside a builder (a byte[], a coder and a count), how it grows, what synchronized really costs in StringBuffer on a modern JVM, why + in a loop is still slow even after Java 9 changed how javac compiles concatenation, and when String.join or StringJoiner is the cleaner tool. Interviewers ask this in nearly every Java screen because the one-line answer ("Buffer is synchronized") is easy, and the follow-ups ("so is Buffer slower? by how much? why does + work fine outside loops?") show whether you've looked under the hood.
Below the line (out of scope)
- Why
Stringitself is immutable: Why String is immutable - Everyday
Stringmethods: Essential String methods - Where literals live and constant folding: How Strings live in memory
- Locks and memory visibility in general (Concurrency module)
The 30-second answer
String is immutable, so every change creates a new object. StringBuilder and StringBuffer are mutable buffers backed by a byte[] (plus a coder for Latin-1 or UTF-16) that grows to old * 2 + 2 characters when it runs out of room, giving amortised O(1) appends. StringBuffer synchronizes every method, so it's safe for single calls from many threads, but compound actions still need external locking and almost nobody shares a builder across threads. On a single thread the JIT often removes those locks, so the gap is small, a few percent to about 25% in my runs. Default to StringBuilder. Since Java 9, a + b + c compiles to one invokedynamic call (JEP 280) that sizes the result exactly, so + is fine for one expression, but += in a loop is still O(n²) and needs a builder.
What you’ll learn
- Explain why repeated
+=on an immutableStringis O(n²) and how a builder makes appends amortised O(1) - Describe the builder's internals:
byte[] value,coder,count, default capacity 16 and growth to old × 2 + 2 - State what
StringBuffer's per-method synchronization guarantees, what it doesn't, and what it costs on JDK 21 - Read the
invokedynamic+StringConcatFactorybytecode thatjavacemits for+since Java 9 (JEP 280) - Choose between
+,StringBuilder,String.join,StringJoinerandCollectors.joiningwith measured numbers
Interview radar
Asked very often: "What's the difference between String, StringBuilder and StringBuffer?" and "Why shouldn't you use + in a loop?"
Senior rounds probe: The growth formula, how JEP 280 changed javac's output, and whether StringBuffer's locks make code thread-safe.
Production angle: A report or log line built with += over 100,000 rows that turns a 1 ms job into a one-second, GC-heavy one.
01Picture it first: a notepad versus printed cards
Think of String as a printed index card. You can't change the text, so "adding a line" means printing a brand-new card with the old text plus the new line, then throwing the old card away. Do that 1,000 times and you've printed 1,000 cards, each longer than the last.
A StringBuilder is a notepad with blank pages. You keep writing at the end. When the pages run out, you get a notepad roughly twice as thick and copy what you have once. At the end you photocopy the finished page into a card (toString()). StringBuffer is the same notepad with a padlock you open and close for every word you write, which only matters if two people try to write on it at once.
Here's what a builder looks like in memory after new StringBuilder().append("order-").append("A1029").
A StringBuilder object holds a reference to a 16-slot byte array with 11 slots used, a LATIN1 coder and count 11; when it fills, capacity grows 16, 34, 70, 142, 286
Notice three things.
- The builder is a small object (24 bytes on 64-bit HotSpot) pointing to a separate
byte[], exactly likeString. The difference is that the builder's array has spare capacity and can be written to. countis the logical length;value.lengthis the capacity.length()returnscount,capacity()returns the array size in characters.- Since Java 9 the buffer is a
byte[]with acoder(LATIN1orUTF16), the same compact representationStringuses. We go deep on that in Interning and compact strings.
02Inside the builder: capacity, growth and coder
Try it yourself first
Before reading on, predict the four numbers this prints.
StringBuilder empty = new StringBuilder();
System.out.println(empty.capacity()); // ?
StringBuilder seeded = new StringBuilder("abc");
System.out.println(seeded.capacity()); // ?
StringBuilder grown = new StringBuilder();
grown.ensureCapacity(17);
System.out.println(grown.capacity()); // ?
StringBuilder log = new StringBuilder();
for (int i = 0; i < 200; i++) log.append('x');
System.out.println(log.capacity()); // ?
16
19
34
286
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Why + in a loop is slow, even on Java 21
- StringBuffer and synchronized: what the lock really buys
- Real world: picking the right tool
- Advanced: edge cases interviewers like
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal
Further reading (9)
- StringBuilder (Java SE 21 API)
- StringBuffer (Java SE 21 API)
- StringConcatFactory (Java SE 21 API)
- StringJoiner (Java SE 21 API)
- JLS §15.18.1: String Concatenation Operator +
- JEP 280: Indify String Concatenation
- JEP 374: Deprecate and Disable Biased Locking
- Effective Java, 3rd ed., Item 63: Beware the performance of string concatenation
- Baeldung: StringBuilder vs StringBuffer in Java