String vs StringBuilder vs StringBuffer
TL;DR
String: Immutable (cannot be changed). Every modification creates a new object. Thread-safe.StringBuffer: Mutable. Thread-safe (all methods are synchronized). Slow.StringBuilder: Mutable. NOT Thread-safe. Fast. (Use this 99% of the time).
Concept
Because String is immutable, performing str = str + "a" inside a loop creates thousands of garbage objects on the heap, killing performance.
To solve this, Java introduced StringBuffer, which maintains a mutable char[] buffer under the hood. You can append data without creating new objects. However, StringBuffer was created in Java 1.0 when thread safety was applied blindly to everything. Every single method in StringBuffer is synchronized, meaning it requires a heavy performance lock every time you append a character.
In Java 1.5, developers realized that building strings is almost always a localized operation done by a single thread. So they released StringBuilder, which is exactly the same as StringBuffer but with all synchronization locks removed, making it significantly faster.
Examples
public class StringPerformanceDemo {
// 1. STRING (Extremely slow in loops)
public String buildString() {
String s = "";
for (int i = 0; i < 10000; i++) {
s += "a"; // Creates 10,000 intermediate String objects in RAM
}
return s;
}
// 2. STRINGBUFFER (Slow, Thread-Safe)
public String buildStringBuffer() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10000; i++) {
sb.append("a"); // Mutates the array. 10,000 synchronization locks acquired/released.
}
return sb.toString();
}
// 3. STRINGBUILDER (Fast, Non-Thread-Safe)
public String buildStringBuilder() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append("a"); // Mutates the array. ZERO locking overhead. Blazing fast.
}
return sb.toString();
}
}
Interview Questions
Q: If String concatenation is slow, why does the compiler let us do String s = "A" + "B" + "C";?
A: For simple, single-line concatenation, the Java compiler automatically optimizes the code for you.
Prior to Java 9, the compiler would quietly convert "A" + "B" + "C" into new StringBuilder().append("A").append("B").append("C").toString() at compile time.
From Java 9 onwards, it uses StringConcatFactory and InvokeDynamic for even faster, advanced runtime concatenation.
You only need to manually use StringBuilder when concatenating inside a loop, where the compiler cannot safely optimize it.
Q: If StringBuilder is not thread-safe, how can a web application handle 100 concurrent users building strings?
A: Thread safety is only an issue when multiple threads share the exact same object reference.
When a web request hits your server, a specific thread executes the controller method. Inside that method, you create a local new StringBuilder(). Because it is a local variable, it is entirely confined to the stack of that single thread (it hasn’t “escaped”). It is impossible for another user’s thread to access it. Therefore, no synchronization is needed.