Question

StringBuilder vs String Concatenation in Java toString()

javaperformancestringconcatenationstringbuilder

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: StringBuilder signals 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.

  • String with + in one short expression: like writing one final label in a single step
  • StringBuilder: 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.

Take Quiz

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

  1. a, b, and c are stored as integers.
  2. Java evaluates the string expression.
  3. The non-string values (a, b, c) are converted to their string form.
  4. The pieces are combined into one final string.
  5. The final result becomes:
"{a:1, b:2, c:3}"
  1. 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 +.

Take Quiz

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

ApproachBest forReadabilityPerformance for small fixed stringsPerformance in loops
+ concatenationShort, simple expressionsHighUsually goodPoor if repeated many times
StringBuilderBuilding strings step by stepMediumFine, but often more verboseUsually much better
String.format()Human-readable formattingHighUsually slowerNot ideal for repeated building

+ vs StringBuilder

  • Use + for short, fixed string assembly
  • Use StringBuilder for repeated appending, especially in loops

StringBuilder vs StringBuffer

TypeThread-safeFaster in single-threaded codeTypical use
StringBuilderNoYesMost application code
StringBufferYesUsually noRare 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
Take Quiz

Cheat Sheet

Quick rules

  • String is immutable
  • + is fine for short, simple string expressions
  • StringBuilder is 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 += value in loops
  • over-optimizing tiny code paths
  • forgetting @Override on toString()

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.

Take Quiz

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 Product class with id, name, and price fields.
  • Override toString() to return a short descriptive string.
  • Create a method that accepts a list of products and returns a multi-line report.
  • Use StringBuilder in the report-building method.
  • Print both individual products and the final report.
Take Quiz

Keep learning

Related questions

View more