StringBuilder vs StringBuffer in Java
StringBuffer is synchronized and legacy.
Twins with one difference
StringBuffer is the original (Java 1.0): a mutable string where **every method is synchronized**. StringBuilder arrived in Java 5 with the same API but no synchronization โ so it's faster.
var b1 = new StringBuffer("ab"); // locks
var b2 = new StringBuilder("ab"); // no locksSame tricks, older dog
StringBuffer has the same methods as StringBuilder. What prints?
var buf = new StringBuffer("12");
buf.append('3').append(4).reverse();
System.out.println(buf);1234432134
Show the answer
4321. append('3') and append(4) give "1234", then reverse() flips it. Same chaining, same behavior as StringBuilder.
What synchronized buys you
A synchronized method lets only one thread at a time run it on that object. Safe for sharing โ but taking the lock costs time on every call, even with a single thread. And builders are almost always local to one method, used by one thread.
Is a chain atomic?
Two threads share a StringBuffer and each runs buf.append("[").append(id).append("]"). Is every [id] guaranteed to appear unbroken?
Think about it, then reveal the answer
No. Each append call is atomic on its own, but a chain is three separate calls โ another thread can slip its append in between. Synchronized methods protect single calls, not sequences. You'd need one explicit lock around the whole chain.
Sharing a StringBuilder
StringBuilder has no locking at all. If two threads append to the same builder at once, they race on its internal buffer: text can get corrupted or an exception can be thrown. The rule: keep each builder thread-confined (local to one method), or synchronize externally.
The cheat sheet
String: immutable text. StringBuilder: mutable, not thread-safe, fast โ the default for building text. StringBuffer: mutable, synchronized, legacy. **+= in a loop**: a new String copy every iteration. Building a message inside one method? StringBuilder, every time.
In real projects
Code reviews often swap StringBuffer for StringBuilder in local code โ free speed. You'll still meet StringBuffer in old APIs: Matcher.appendReplacement accepted only a StringBuffer until Java 9 added a StringBuilder overload.
Key takeaways
- StringBuffer: synchronized, legacy
- StringBuilder: not synchronized, faster, preferred
- Same methods: append, insert, reverse, delete...
- Synchronized methods do not make a sequence of calls atomic
StringBuilder's own Javadoc says it was designed as a drop-in replacement for StringBuffer "in places where the string buffer was being used by a single thread (as is generally the case)" โ the locks were usually pure overhead.
Practice questions
What does this print?
var buf = new StringBuffer("ab");
buf.append(1).append('c').reverse();
System.out.println(buf);- ab1c
- c1ba
- cba1
- 1cba
Check your answer
c1ba. StringBuffer has the same API as StringBuilder: append gives "ab1c", then reverse gives "c1ba".
You build a message inside one method and return it. Which class should you use?
- StringBuilder
- StringBuffer
- String with += in a loop
- char[] with manual resizing
Check your answer
StringBuilder. A local builder is only used by one thread, so StringBuffer's locking is wasted work. StringBuilder is the modern default.