StringBuilder and StringBuffer in Java

When text is built piece by piece, a mutable buffer avoids the copying that repeated String concatenation forces.

Why they exist

String is immutable, so every concatenation creates a new object and copies the characters. Building text in a loop therefore does work proportional to the square of the final length. StringBuilder and StringBuffer are mutable character buffers that append in place instead.

The problem, measured in objects

String result = "";
for (int i = 0; i < 5; i++) {
    result += i;      // creates "0", "01", "012", "0123", "01234"
}

Five appends created five strings and copied every earlier character each time. At five iterations this is invisible; at fifty thousand it dominates the program.

StringBuilder

StringBuilder builder = new StringBuilder();

for (int i = 0; i < 5; i++) {
    builder.append(i);
}

String result = builder.toString();   // "01234"

The buffer grows only when its capacity runs out, and appending writes into the existing array. One object is created, not five.

The main methods

StringBuilder sb = new StringBuilder("Java");

sb.append(" Notes");            // Java Notes
sb.insert(0, "Core ");          // Core Java Notes
sb.replace(0, 4, "Basic");      // Basic Java Notes
sb.delete(0, 6);                // Java Notes
sb.reverse();                   // setoN avaJ
sb.setLength(4);                // setoN avaJ trimmed to seto

System.out.println(sb.length());
System.out.println(sb.capacity());     // allocated size, at least the length
System.out.println(sb.indexOf("e"));

Every mutating method returns the same builder, so calls chain naturally:

String line = new StringBuilder()
        .append("Order ").append(1042)
        .append(" total ").append(2499.50)
        .toString();

StringBuffer

StringBuffer has the same API but every method is synchronized, which makes an instance safe to share across threads. That synchronisation costs time on every call, and it is unnecessary for a buffer used inside a single method.

Comparison

AspectStringStringBuilderStringBuffer
MutableNoYesYes
Thread safeYes, by immutabilityNoYes, synchronized
Speed for appendsSlowestFastestSlower than builder
IntroducedJava 1.0Java 5Java 1.0
Use whenValues are fixedBuilding text locallyA buffer is genuinely shared
The practical rule: use String by default, StringBuilder when building text, and StringBuffer almost never. A shared mutable buffer is usually a design problem rather than a threading requirement.

Capacity

StringBuilder sb = new StringBuilder(1024);   // pre-sized, avoids regrowth

When the final size is roughly known, supplying it prevents repeated array growth and copying. This matters only in hot code, so measure before tuning.

equals is not overridden

StringBuilder a = new StringBuilder("x");
StringBuilder b = new StringBuilder("x");

System.out.println(a.equals(b));                        // false - identity
System.out.println(a.toString().equals(b.toString()));  // true

Neither builder overrides equals or hashCode, so they must never be used as map keys or set elements.

A practical example

public static String toCsvRow(List<String> fields) {
    StringBuilder row = new StringBuilder();

    for (int i = 0; i < fields.size(); i++) {
        if (i > 0) {
            row.append(',');
        }
        row.append(fields.get(i).replace(",", " "));
    }
    return row.toString();
}

For this particular job String.join is simpler, and for streams Collectors.joining is simpler still. Reach for a builder when the assembly logic is more involved than a plain join.

Common mistakes

  • Using StringBuffer out of habit and paying for synchronisation that is never needed.
  • Calling toString inside the loop instead of once at the end.
  • Comparing builders with equals.
  • Using a builder for a single concatenation, which is slower and harder to read than +.
  • Confusing capacity with length.

Best practices

  • Keep the builder local to the method that fills it.
  • Pre-size it when the result length is predictable and the code is hot.
  • Prefer String.join or Collectors.joining for simple separator work.
  • Convert to String once, at the end.

Practice

  1. Rewrite a loop that concatenates 10000 numbers using a builder, and explain the difference in objects created.
  2. Why does new StringBuilder("abc").reverse() change the object rather than returning a new one?
  3. What does capacity() report for new StringBuilder("Java"), and why is it larger than 4?
  4. Write a method that builds a formatted address block from several optional fields, skipping the blank ones.
  5. Explain in one sentence when StringBuffer would genuinely be the right choice.

Conclusion

Immutable strings are the right default, and a mutable builder is the right tool for assembling text. Use StringBuilder locally, use StringBuffer only when a buffer is truly shared between threads, and convert to a String once at the end.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Java notes →
Java

Arrays in Java

An array is a fixed size object holding elements of one type, indexed from zero, with the length fixed at creation.

Read more
Java

Strings in Java

String is an immutable object with a shared literal pool, and both facts explain how comparison, concatenation and performance behave.

Read more
Java

Introduction to Java

Java is a statically typed, object oriented language that compiles to bytecode and runs on a virtual machine, which is what makes it portable.

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.