By WebNest Studio Engineering Team
Java - Core Tutorial
String vs StringBuffer vs StringBuilder
String, StringBuffer, and StringBuilder all represent sequences of characters, but they differ fundamentally in mutability and thread-safety, and choosing the right one for a given situation is a common Java design decision — and a frequent interview question.
Why it matters
Choosing between these three types is a decision you make constantly, usually without thinking. The right default is simple — String for values, StringBuilder for building — but you should be able to explain why, because "String vs StringBuffer vs StringBuilder" is asked in nearly every Java interview for junior roles.
Mutability: The Core Difference
String is immutable: every "modification" produces a brand-new object, leaving the original unchanged. StringBuffer and StringBuilder are both mutable: they maintain an internal, resizable character buffer that is changed directly by methods like append() and insert(), without creating a new object each time. This is why building a large piece of text with String concatenation in a loop is inefficient compared to using StringBuffer or StringBuilder — the loop would otherwise create one throwaway String object per iteration.
Thread Safety and Performance
StringBuffer's methods are synchronized, making a single instance safe to mutate from multiple threads, but that safety costs performance due to lock acquisition on every call. StringBuilder offers the identical API without synchronization, making it faster in single-threaded contexts — which describes the vast majority of string-building code — but unsafe to share across threads without external coordination.
- String: immutable, inherently thread-safe (nothing can change), best for fixed or rarely-changing text and as map/set keys.
- StringBuffer: mutable, synchronized (thread-safe), best when multiple threads must modify the same buffer.
- StringBuilder: mutable, not synchronized, best for single-threaded string building — the usual default.
Choosing the Right One
As a practical rule: use String for values that do not change or change rarely, use StringBuilder when you need to build or transform text repeatedly within one thread (loops, parsing, formatting), and reach for StringBuffer only in the specific case where the same mutable buffer genuinely must be shared and modified by multiple threads concurrently.
Time complexity
| Operation | Average | Worst case |
|---|---|---|
| Joining n pieces with String += in a loop | O(n²) characters copied in total | O(n²) |
| Joining n pieces with StringBuilder.append | O(n) in total | O(n) |
Example
Same task, three types, and a rough performance intuition
public class StringComparisonDemo {
public static void main(String[] args) {
// String: each += creates a new object (fine for a handful of operations)
String s = "";
for (int i = 0; i < 3; i++) {
s += i;
}
System.out.println("String result: " + s);
// StringBuilder: one buffer, modified in place, no synchronization
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++) {
sb.append(i);
}
System.out.println("StringBuilder result: " + sb);
// StringBuffer: one buffer, modified in place, synchronized (thread-safe)
StringBuffer sbf = new StringBuffer();
for (int i = 0; i < 3; i++) {
sbf.append(i);
}
System.out.println("StringBuffer result: " + sbf);
}
}
String result: 012
StringBuilder result: 012
StringBuffer result: 012
Common Mistakes
- Defaulting to StringBuffer everywhere "to be safe," paying for synchronization in single-threaded code that never needed it.
- Using String concatenation with "+" inside large loops, which silently creates many intermediate objects and degrades performance as data grows.
- Assuming StringBuilder is thread-safe because it looks and behaves just like StringBuffer — it explicitly is not.
Exercise
This program builds its result with String concatenation in a loop. Rewrite it to use a StringBuilder. The output must stay the same.
Expected output: 12345
public class BuildDigits {
public static void main(String[] args) {
String result = "";
for (int i = 1; i <= 5; i++) {
result += i;
}
System.out.println(result);
}
}
Show hints
- Create the StringBuilder before the loop and call
append(i)inside it. - Convert to a String once, at the end, with
toString().
Show solution
public class BuildDigits {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder();
for (int i = 1; i <= 5; i++) {
builder.append(i);
}
String result = builder.toString();
System.out.println(result); // 12345
}
}
Quick quiz
1. Which of the three types is immutable?
- A. String
- B. StringBuffer
- C. StringBuilder
- D. All of them
Show answer
A. Only String is immutable. StringBuffer and StringBuilder can be modified in place.
2. You are building a long piece of text inside one method, on one thread. Which is the best choice?
- A. StringBuilder
- B. StringBuffer
- C. String
- D. char
Show answer
A. StringBuilder is mutable and has no locking overhead, so it is the fastest option when only one thread is involved.
3. Which type has synchronized methods?
- A. String
- B. StringBuilder
- C. StringBuffer
- D. None of them
Show answer
C. StringBuffer synchronizes its methods. String needs no synchronization because it cannot change.
Interview questions
What are the differences between String, StringBuffer and StringBuilder?
Show answer
String is immutable: every modification creates a new object, which makes it safe to share but costly to build up piece by piece. StringBuffer is mutable and its methods are synchronized, so it is thread-safe but slower. StringBuilder is mutable with the same methods and no synchronization, so it is the fastest but not thread-safe.
In practice: String for values that do not change, StringBuilder for building text, and StringBuffer only when several threads really modify the same buffer.
Do StringBuilder and StringBuffer override equals()?
Show answer
No. They inherit equals() from Object, so two builders with the same characters are not equal unless they are the same object. To compare content, convert to Strings first: a.toString().equals(b.toString()).
Key Points to Remember
- String is immutable; StringBuffer and StringBuilder are mutable character buffers.
- StringBuffer is synchronized (thread-safe but slower); StringBuilder is not synchronized (faster, single-threaded use).
- Prefer StringBuilder by default for building text; use StringBuffer only for genuine multi-threaded mutation; use String for fixed or rarely-changed values.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering