StringBuilder
programming
Java
coding best practices
software development

How to use StringBuilder wisely?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

String concatenation in Java is easy to write, but repeated concatenation can quietly allocate many temporary objects. StringBuilder exists to make repeated edits cheap by storing characters in a mutable buffer. The class is simple, but using it well means understanding when it helps, when it does not, and how to avoid unnecessary work.

When StringBuilder Actually Helps

String is immutable, so an expression such as result = result + part produces a new string each time. In short code that does not matter much, and the Java compiler already optimizes some constant concatenation. The real benefit appears when you build a value inside a loop, while parsing input, or when many small fragments must be joined.

java
1public class JoinWithPlus {
2    public static void main(String[] args) {
3        String result = "";
4        for (int i = 0; i < 5; i++) {
5            result += i;
6            if (i < 4) {
7                result += ",";
8            }
9        }
10        System.out.println(result);
11    }
12}

That example is readable, but it creates extra intermediate strings. A StringBuilder keeps a single growing buffer instead.

java
1public class JoinWithBuilder {
2    public static void main(String[] args) {
3        StringBuilder builder = new StringBuilder();
4        for (int i = 0; i < 5; i++) {
5            builder.append(i);
6            if (i < 4) {
7                builder.append(",");
8            }
9        }
10        System.out.println(builder.toString());
11    }
12}

The improvement matters most when the loop is large or runs often.

Pick the Right String Tool

Not every concatenation needs StringBuilder. A plain String is fine for a few values in one expression.

java
String message = "User " + userId + " logged in";

For multi-step assembly in a single thread, use StringBuilder.

java
1StringBuilder sql = new StringBuilder();
2sql.append("SELECT id, name ");
3sql.append("FROM users ");
4sql.append("WHERE active = true");
5System.out.println(sql);

For synchronized access across threads, Java also provides StringBuffer, but most application code should not share a mutable text buffer between threads in the first place. If the data is already in a collection, String.join or Collectors.joining can be even cleaner than manual appends.

java
1import java.util.List;
2
3public class JoinNames {
4    public static void main(String[] args) {
5        List<String> names = List.of("Ada", "Linus", "Grace");
6        System.out.println(String.join(" | ", names));
7    }
8}

Use the simplest tool that matches the job. StringBuilder is a performance tool, not a default replacement for every string literal.

Pre-Size the Buffer for Large Builds

StringBuilder grows automatically, but growth means allocating a bigger internal array and copying characters. If you have a rough size estimate, pass an initial capacity.

java
1public class CsvBuilder {
2    public static void main(String[] args) {
3        String[] columns = {"id", "email", "status", "created_at"};
4        StringBuilder builder = new StringBuilder(128);
5
6        for (int i = 0; i < columns.length; i++) {
7            if (i > 0) {
8                builder.append(',');
9            }
10            builder.append(columns[i]);
11        }
12
13        System.out.println(builder);
14    }
15}

This is not a magic optimization, but for large loops it removes some avoidable resizing. Estimate loosely; there is no need to calculate the exact character count.

Build in One Direction and Convert Once

A common mistake is calling toString() too early and then continuing to append elsewhere. Keep data in the builder until the final result is needed.

java
1public class LogLine {
2    public static void main(String[] args) {
3        String level = "INFO";
4        String path = "/health";
5        int status = 200;
6
7        StringBuilder builder = new StringBuilder(64);
8        builder.append('[').append(level).append("] ");
9        builder.append("GET ").append(path).append(' ');
10        builder.append("status=").append(status);
11
12        String line = builder.toString();
13        System.out.println(line);
14    }
15}

The fluent append style keeps code compact and avoids intermediate strings. It is also safer than mixing many + fragments over several lines where delimiters are easy to miss.

Reuse Carefully

Reusing a builder with setLength(0) can be useful inside hot loops, but only when the lifecycle is obvious. A reused builder that leaks between methods becomes harder to reason about than simply creating a new one.

java
1public class BatchFormatter {
2    public static void main(String[] args) {
3        StringBuilder builder = new StringBuilder(64);
4        String[] values = {"A12", "B34", "C56"};
5
6        for (String value : values) {
7            builder.setLength(0);
8            builder.append("item=").append(value).append(";valid=true");
9            System.out.println(builder);
10        }
11    }
12}

If reuse makes control flow unclear, skip it. Readability usually wins unless profiling shows string building is a real bottleneck.

Common Pitfalls

  • Replacing every String expression with StringBuilder even when a simple one-line concatenation is clearer.
  • Forgetting to pre-size the buffer in large loops where repeated growth becomes measurable.
  • Calling toString() repeatedly during construction instead of converting once at the end.
  • Sharing one mutable builder across threads instead of keeping it local to a method or task.
  • Reusing a builder with setLength(0) in ways that make stale content bugs harder to spot.

Summary

  • Use StringBuilder when text is assembled incrementally, especially inside loops.
  • Keep plain String concatenation for short, simple expressions.
  • Consider String.join or stream joining when the input is already a collection.
  • Set an initial capacity when the output is large enough that resizing is likely.
  • Convert with toString() once, near the point where the final string is needed.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.