Question
StringBuilder vs StringBuffer in Java: Differences and Performance
Question
In Java, what is the main difference between StringBuffer and StringBuilder?
Are there any performance considerations when choosing one over the other?
Short Answer
By the end of this page, you will understand how StringBuilder and StringBuffer work in Java, why both exist, what thread safety means in this context, and how their performance differs. You will also learn when to use each one in real programs.
Concept
StringBuilder and StringBuffer are both mutable sequence classes in Java. They are used when you need to build or modify strings efficiently.
Java String objects are immutable, which means once a string is created, it cannot be changed. If you repeatedly combine strings with +, Java often creates many temporary objects. That can be inefficient in loops or repeated text-building tasks.
That is where StringBuilder and StringBuffer help.
Core difference
The main difference is thread safety:
StringBufferis synchronized, so it is thread-safe.StringBuilderis not synchronized, so it is not thread-safe.
Because StringBuffer adds synchronization, it usually has more overhead. In single-threaded code, StringBuilder is typically faster.
Why this matters
In real programming, choosing between them depends on whether multiple threads may modify the same object at the same time.
- If only one thread uses the object,
StringBuilderis usually the right choice. - If multiple threads share and modify the same mutable character sequence,
StringBufferprovides built-in synchronization.
In practice, StringBuilder is more commonly used because many string-building tasks happen inside a method or request where the object is not shared across threads.
Important note
Thread safety is not automatically needed just because your application is multithreaded. The question is whether the same instance is shared and modified by multiple threads. If not, StringBuilder is still fine.
Mental Model
Think of both classes as editable notepads:
StringBuilderis a regular notepad on your desk. It is quick and easy to write on, but if several people try to write on it at the same time, the result may become messy.StringBufferis the same notepad, but with a rule: only one person can write at a time. That makes it safer for shared use, but slower because people must wait their turn.
So the trade-off is simple:
- Speed:
StringBuilder - Safety for shared access:
StringBuffer
Syntax and Examples
Both classes have very similar APIs.
Basic syntax
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
String result = sb.toString();
StringBuffer sbf = new StringBuffer();
sbf.append("Hello");
sbf.append(" ");
sbf.append("World");
String result = sbf.toString();
Example with StringBuilder
public class Main {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder();
builder.append("Java");
builder.append(" is ");
builder.append("fast");
System.out.println(builder.toString());
}
}
Output:
Java is fast
This is efficient because the same mutable object is updated instead of creating a new String each time.
Example with StringBuffer
public class Main {
public static void main(String[] args) {
StringBuffer buffer = new StringBuffer();
buffer.append("Safe");
buffer.append(" text");
System.out.println(buffer.toString());
}
}
Output:
Safe text
The usage looks almost identical. The main difference is not syntax, but synchronization behavior.
Step by Step Execution
Consider this example:
StringBuilder builder = new StringBuilder();
builder.append("A");
builder.append("B");
builder.append("C");
String result = builder.toString();
Step by step:
new StringBuilder()creates an empty mutable character container.append("A")addsAto the container.- Current content:
A
- Current content:
append("B")addsBto the end.- Current content:
AB
- Current content:
append("C")addsCto the end.- Current content:
ABC
- Current content:
toString()creates a regular immutableStringcontainingABC.resultnow refers to the final string:"ABC".
Now compare that idea to repeated string concatenation:
String text = "";
text = text + "A";
text = text + "B";
text = text + "C";
Conceptually, this creates new String objects as the value changes, because String is immutable. That is why builders are often preferred for repeated updates.
Real World Use Cases
Here are common situations where StringBuilder or StringBuffer is useful:
Building API responses or messages
StringBuilder message = new StringBuilder();
message.append("User: ").append(username).append(" logged in at ").append(time);
Generating CSV or text files
StringBuilder csv = new StringBuilder();
csv.append("id,name\n");
csv.append("1,Alice\n");
csv.append("2,Bob\n");
Building SQL or dynamic queries carefully
StringBuilder query = new StringBuilder("SELECT * FROM users WHERE 1=1");
if (activeOnly) {
query.append(" AND active = true");
}
Creating logs efficiently inside loops
StringBuilder log = new StringBuilder();
for (int i = 0; i < 3; i++) {
log.append("Step ").append(i).append(" completed\n");
}
Shared legacy code with synchronization needs
Older or shared code may still use StringBuffer when multiple threads are expected to modify the same buffer.
Real Codebase Usage
In real Java codebases, developers usually follow these patterns:
Use StringBuilder for local string construction
Inside a method, if the object is created and used only there, StringBuilder is the usual choice.
public String formatName(String firstName, String lastName) {
StringBuilder builder = new StringBuilder();
builder.append(lastName).append(", ").append(firstName);
return builder.toString();
}
Use chaining for readability
String result = new StringBuilder()
.append("Total: ")
.append(42)
.append(" items")
.toString();
Pre-size when large output is expected
If you know the text will be large, you can reduce resizing overhead.
StringBuilder builder = new StringBuilder(1000);
Avoid sharing mutable builders across threads
A common pattern is to keep builders local rather than shared. This avoids synchronization issues and keeps code simpler.
Prefer other synchronization strategies when appropriate
Even in multithreaded applications, developers often avoid sharing a mutable buffer at all. Instead of choosing StringBuffer, they may redesign the code so each thread uses its own StringBuilder. That is often simpler and faster.
Validation and formatting
Builders are often used after guard clauses and validation logic.
public String createLabel(String name, int id) {
if (name == null || name.isBlank()) {
return "Invalid name";
}
return new StringBuilder()
.append("User[")
.append(id)
.append("]: ")
.append(name)
.toString();
}
Common Mistakes
1. Thinking StringBuffer is always better because it is thread-safe
Thread safety is useful only when the same object is actually shared between threads.
If the buffer is local to one method, StringBuilder is usually the better default.
2. Using StringBuilder in shared mutable multithreaded code
Broken example:
public class SharedText {
private static StringBuilder builder = new StringBuilder();
public static void addText(String text) {
builder.append(text);
}
}
If multiple threads call addText, the result may be corrupted or unpredictable.
3. Assuming + is always bad
For small, simple concatenations, + is often perfectly fine and more readable.
String fullName = firstName + " " + lastName;
You usually switch to StringBuilder when concatenation is repeated, especially inside loops.
4. Forgetting to call toString()
StringBuilder is not the same type as String.
Broken example:
StringBuilder builder = new StringBuilder();
builder.append("Hello");
// String text = builder; // compile error
Correct:
String text = builder.toString();
5. Sharing a mutable builder by accident
Even if your app uses multiple threads, local variables are usually safe because each method call gets its own variable.
Safer example:
public String buildMessage(String name) {
StringBuilder builder = new StringBuilder();
builder.append("Hello ").append(name);
return builder.toString();
}
6. Ignoring capacity for very large builds
If you append a lot of content, the internal array may resize multiple times. You can reduce that by setting an initial capacity.
StringBuilder builder = new StringBuilder(5000);
Comparisons
| Feature | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutable | No | Yes | Yes |
| Thread-safe | Yes, because immutable | No | Yes |
| Synchronized methods | No | No | Yes |
| Usually faster for repeated modification | No | Yes | Usually slower than StringBuilder |
| Best use case | Fixed text values | Single-threaded string building | Shared mutable string building |
StringBuilder vs StringBuffer
| Aspect | StringBuilder | StringBuffer |
|---|---|---|
| Synchronization | No | Yes |
| Performance | Faster in most single-threaded cases | Slower due to synchronization overhead |
| Thread safety | Not safe for shared concurrent modification | Safe for shared concurrent modification |
| Common modern default | Yes | Less common |
+ vs builder classes
| Situation | Best choice |
|---|---|
| One or two simple concatenations | + |
| Repeated concatenation in a loop | StringBuilder |
| Shared mutable text across threads | StringBuffer or redesign to avoid sharing |
Cheat Sheet
Quick rule
- Use
StringBuilderby default for repeated string modification. - Use
StringBufferonly when the same mutable buffer must be shared safely across threads.
Core difference
StringBuilder builder = new StringBuilder(); // not synchronized
StringBuffer buffer = new StringBuffer(); // synchronized
Common methods
append()
insert()
delete()
reverse()
length()
capacity()
toString()
Example
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" ").append("Java");
String result = sb.toString();
Performance rule
StringBuilderis usually faster.StringBufferhas synchronization overhead.- For small simple expressions,
+is often fine.
Best practices
- Keep builders local to methods.
- Use
toString()to get aString. - Set initial capacity for large outputs.
StringBuilder sb = new StringBuilder(1000);
Edge case to remember
A multithreaded application does not automatically require StringBuffer.
It is only needed if the same instance is shared and modified by multiple threads.
FAQ
What is the main difference between StringBuilder and StringBuffer in Java?
StringBuffer is synchronized and thread-safe. StringBuilder is not synchronized and is usually faster.
Is StringBuilder faster than StringBuffer?
Yes, in most single-threaded cases StringBuilder is faster because it does not pay synchronization cost.
When should I use StringBuffer?
Use it when multiple threads must safely modify the same mutable character sequence.
Should I always use StringBuilder instead of String?
No. For simple one-off concatenations, regular String with + is often clearer and perfectly acceptable.
Is StringBuilder thread-safe if each thread creates its own instance?
Yes. If each thread uses its own separate instance, there is no shared mutable state problem.
Why are strings immutable in Java?
Immutability improves safety, predictability, and makes strings easier to share and optimize.
Do StringBuilder and StringBuffer have the same methods?
They have very similar APIs, including methods like append(), insert(), delete(), and toString().
Mini Project
Description
Build a small Java utility that generates a formatted order summary. This project demonstrates why StringBuilder is useful when combining many pieces of text into one final output.
Goal
Create a method that builds a multi-line receipt string efficiently using StringBuilder.
Requirements
[ "Create a method that accepts a customer name and a list of item names.", "Build a multi-line receipt using StringBuilder.", "Include a header, each item on its own line, and a total item count.", "Return the final result as a String." ]
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.