Question
StringBuilder vs String Concatenation in Java toString()
Question
In Java, which of these two toString() implementations is preferred?
public String toString() {
return "{a:" + a + ", b:" + b + ", c:" + c + "}";
}
or
public String toString() {
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:").append(b)
.append(", c:").append(c)
.append("}")
.toString();
}
If the class has only three properties, the performance difference may be small. More generally, at what point should you choose StringBuilder instead of + string concatenation in Java?
Short Answer
By the end of this page, you will understand how Java handles string concatenation, why + is often fine inside a simple toString() method, and when StringBuilder becomes the better choice. You will also learn how readability, loops, and repeated concatenation affect the decision.
Concept
In Java, String objects are immutable, which means their contents cannot be changed after creation. Every time you combine strings, Java must produce a new string value.
That sounds like + would always be inefficient, but there is an important detail: for a single expression such as this:
return "{a:" + a + ", b:" + b + ", c:" + c + "}";
Java compilers typically translate it into code that behaves much like using a StringBuilder internally. So in simple cases, especially inside one return statement, + is usually both readable and efficient enough.
StringBuilder matters more when you are doing repeated concatenation over time, especially inside loops or across multiple statements.
For example, this is a common performance problem:
String result = "";
for (String item : items) {
result += item;
}
Each iteration creates new intermediate strings. That can become expensive.
A better approach is:
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item);
}
String result = sb.toString();
Why this matters
Choosing between + and StringBuilder is not just about micro-optimization.
It affects:
- Readability: simple code is easier to maintain
- Performance: repeated concatenation can waste memory and CPU
- Intent:
StringBuildersignals that a string is being assembled piece by piece
For a small toString() with a few fields, + is usually preferred because it is shorter and clearer.
Mental Model
Think of a String as a sealed label. Once written, you cannot edit it; you must make a brand-new label if you want different text.
Think of StringBuilder as a whiteboard. You can keep adding text to it without throwing the whole thing away each time.
Stringwith+in one short expression: like writing one final label in a single stepStringBuilder: like building a message gradually on a whiteboard
So the question is not "Is StringBuilder always faster?" The better question is:
Am I building one small string once, or am I repeatedly growing a string over many steps?
If it is one small string once, + is usually fine.
If it grows repeatedly, StringBuilder is usually the right tool.
Syntax and Examples
Basic + concatenation
String name = "Sam";
int age = 25;
String text = "Name: " + name + ", Age: " + age;
This is the simplest way to combine values into one string.
Using StringBuilder
String name = "Sam";
int age = 25;
StringBuilder sb = new StringBuilder();
sb.append("Name: ");
sb.append(name);
sb.append(", Age: ");
sb.append(age);
String text = sb.toString();
This is useful when the string is built in multiple steps.
Simple toString() example
For a typical Java object, this is often the clearest style:
@Override
public String toString() {
return "{a:" + a + ", b:" + b + ", c:" + c + "}";
}
Why this is usually preferred
- It is short
- It is easy to read
- The compiler can optimize simple concatenation expressions
- For a small number of fields, performance differences are usually negligible
Loop example where StringBuilder is better
List<String> words = List.of("Java", "is", "fun");
StringBuilder sb = new StringBuilder();
for (String word : words) {
sb.append(word).append(" ");
}
String sentence = sb.toString().trim();
System.out.println(sentence);
This avoids creating many temporary strings during the loop.
Step by Step Execution
Consider this code:
int a = 1;
int b = 2;
int c = 3;
String text = "{a:" + a + ", b:" + b + ", c:" + c + "}";
System.out.println(text);
Step-by-step
a,b, andcare stored as integers.- Java evaluates the string expression.
- The non-string values (
a,b,c) are converted to their string form. - The pieces are combined into one final string.
- The final result becomes:
"{a:1, b:2, c:3}"
System.out.println(text);prints that result.
Important detail
Although the source code uses +, Java often compiles this kind of expression into something efficient behind the scenes.
Now compare that with repeated concatenation:
String result = "";
result += "A";
result += "B";
result += "C";
What happens conceptually
- Start with
"" - Create a new string for
"A" - Create another new string for
"AB" - Create another new string for
"ABC"
That repeated rebuilding is why StringBuilder becomes useful when concatenation happens many times.
Real World Use Cases
StringBuilder and + both appear in real Java programs, but in different situations.
When + is commonly used
- Simple
toString()methods - Logging messages with a few values
- Exception messages
- Building short labels or user-facing text
- Small return expressions
Example:
throw new IllegalArgumentException("Invalid id: " + id);
When StringBuilder is commonly used
- Building CSV content
- Generating large text reports
- Creating SQL strings dynamically
- Producing formatted output in loops
- Assembling JSON-like text manually
- Processing file content line by line
Example:
StringBuilder csv = new StringBuilder();
for (User user : users) {
csv.append(user.getId())
.append(",")
.append(user.getName())
.append("\n");
}
In APIs and backend code
You may use StringBuilder when:
- combining many values in serializers
- constructing debug output
- generating long response bodies manually
- assembling template-like text in batches
For short, fixed-size strings, developers usually keep the code simple and readable with +.
Real Codebase Usage
In real projects, developers usually choose based on clarity first, then optimize only where needed.
Common pattern: simple toString() uses +
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', active=" + active + "}";
}
This is very common because the structure is fixed and short.
Common pattern: loops use StringBuilder
public String joinNames(List<String> names) {
StringBuilder sb = new StringBuilder();
for (String name : names) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(name);
}
return sb.toString();
}
Guard clause plus building output
public String formatUser(User user) {
if (user == null) {
return "<null user>";
}
return "User: " + user.getName();
}
Developers often combine simple early returns with short concatenation.
Validation and repeated output building
public String validate(List<String> errors) {
if (errors.isEmpty()) {
return "OK";
}
StringBuilder sb = new StringBuilder("Validation errors:\n");
for (String error : errors) {
sb.append("- ").append(error).append("\n");
}
return sb.toString();
}
Pre-sizing a builder
Sometimes developers provide a capacity when they expect larger output:
StringBuilder sb = new StringBuilder(256);
This can reduce resizing, but for small strings it is often unnecessary. In your toString() example, new StringBuilder(100) is usually not needed unless you have measured a real benefit.
Common Mistakes
1. Using StringBuilder everywhere for tiny strings
Beginners sometimes assume StringBuilder is always better.
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("x=").append(x);
return sb.toString();
}
This works, but for very small fixed strings, it can be more verbose without meaningful benefit.
Better idea
Use the simpler version unless repeated concatenation is a real concern.
public String toString() {
return "x=" + x;
}
2. Repeated + inside a loop
String result = "";
for (String item : items) {
result += item;
}
Problem
This creates many intermediate strings.
Fix
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item);
}
String result = sb.toString();
3. Guessing performance without measuring
It is easy to over-optimize code that runs rarely.
Better approach
- Prefer readable code first
- Optimize hotspots only when profiling shows a problem
4. Forgetting @Override on toString()
public String tostring() {
return "...";
}
Problem
This does not override Object.toString() because the method name is wrong.
Fix
@Override
public String toString() {
return "...";
}
5. Manually formatting complex text when a library already exists
For JSON, XML, or SQL, manual concatenation can become error-prone.
Better idea
Use appropriate libraries when the structure is complex.
6. Assuming there is a magic number of fields
There is no exact rule like "switch at 5 fields" or "switch at 10 fields."
The real decision depends on:
- whether the concatenation is in one expression or many iterations
- how often the code runs
- how large the final string becomes
- whether profiling shows it matters
Comparisons
| Approach | Best for | Readability | Performance for small fixed strings | Performance in loops |
|---|---|---|---|---|
+ concatenation | Short, simple expressions | High | Usually good | Poor if repeated many times |
StringBuilder | Building strings step by step | Medium | Fine, but often more verbose | Usually much better |
String.format() | Human-readable formatting | High | Usually slower | Not ideal for repeated building |
+ vs StringBuilder
- Use
+for short, fixed string assembly - Use
StringBuilderfor repeated appending, especially in loops
StringBuilder vs StringBuffer
| Type | Thread-safe | Faster in single-threaded code | Typical use |
|---|---|---|---|
StringBuilder | No | Yes | Most application code |
StringBuffer | Yes | Usually no | Rare cases needing synchronization |
+ vs String.format()
String text = String.format("{a:%d, b:%d}", a, b);
String.format() can be readable for formatted output, but it is usually more expensive than simple concatenation and is not typically preferred for basic toString() methods.
Practical rule
- Single small expression: use
+ - Many appends over time: use
StringBuilder - Need formatted templates: consider
String.format()if readability matters more than speed
Cheat Sheet
Quick rules
Stringis immutable+is fine for short, simple string expressionsStringBuilderis better for repeated concatenation- In loops, prefer
StringBuilder - There is no fixed number of fields where you must switch
- For small
toString()methods, prefer readability
Common toString() style
@Override
public String toString() {
return "{a:" + a + ", b:" + b + ", c:" + c + "}";
}
Common loop style
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item);
}
String result = sb.toString();
When to choose StringBuilder
- inside loops
- when appending in many statements
- when building long text
- when performance profiling shows string creation matters
When + is enough
- one return statement
- short log or error messages
- simple
toString()methods - fixed, small output
Capacity hint
StringBuilder sb = new StringBuilder(100);
Use a capacity only if you have a reason to estimate the size. For small strings, it is usually optional.
Watch out for
- repeated
result += valuein loops - over-optimizing tiny code paths
- forgetting
@OverrideontoString()
FAQ
Should I always use StringBuilder instead of + in Java?
No. For small, simple expressions, + is usually clearer and perfectly acceptable.
Is + inside toString() inefficient?
Usually not for short fixed expressions. Java commonly optimizes simple concatenation.
At how many fields should I switch to StringBuilder?
There is no exact number. The bigger factor is whether the string is built once in a single expression or repeatedly over many steps.
Why is StringBuilder better in loops?
Because it avoids creating a new String object on every iteration.
Is String.format() better than + for toString()?
Usually not for performance. It can be readable, but it is generally heavier than simple concatenation.
Should I give StringBuilder an initial capacity?
Only when you have a reasonable size estimate or are building larger strings often. For small strings, this is usually unnecessary.
What is the difference between StringBuilder and StringBuffer?
StringBuffer is synchronized and thread-safe, while StringBuilder is not. StringBuilder is usually preferred in normal single-threaded code.
Mini Project
Description
Build a small Java class that represents a product and implements toString() in a clean way. Then create another method that builds a long report for multiple products using StringBuilder. This project demonstrates the practical difference between short fixed string creation and repeated string building in a loop.
Goal
Create a class with a readable toString() method using +, and a reporting method that uses StringBuilder to efficiently combine many product lines.
Requirements
- Create a
Productclass withid,name, andpricefields. - Override
toString()to return a short descriptive string. - Create a method that accepts a list of products and returns a multi-line report.
- Use
StringBuilderin the report-building method. - Print both individual products and the final report.
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.