Traps

88. String Concatenation in a Loop

Using + to concatenate strings inside a loop is O(n²) because each + creates a new String; use StringBuilder explicitly in complex cases, or String.join() / Collectors.joining() for collections.

Java 21 (core language)

Explanation

String is immutable. Every + operation creates a new String object (conceptually — the JVM may optimise simple cases). In a loop of n iterations that concatenates: iteration 1 copies 1 char, iteration 2 copies 2 chars, ..., iteration n copies n chars. Total copies: 1+2+...+n = n(n+1)/2 — O(n²) time and O(n²) temporary objects.

With StringBuilder, the backing array grows geometrically (amortised O(1) per append), so building a string of n chars is O(n) total.

Modern alternatives: String.join(delimiter, elements) for arrays/iterables; Collectors.joining(delimiter, prefix, suffix) in streams; String.repeat(n) for repetition.

Since Java 9, the JVM uses invokedynamic + StringConcatFactory to optimise simple + expressions — but this only applies to constant numbers of operands known at compile time, not loops. Loop concatenation still requires StringBuilder.

Example

import java.util.List;
import java.util.stream.Collectors;

public class StringConcatInLoop {
    public static void main(String[] args) {
        int N = 10_000;

        // BAD: O(n^2) — creates a new String each iteration
        long t1 = System.nanoTime();
        String bad = "";
        for (int i = 0; i < N; i++) {
            bad += "x"; // new String created every iteration
        }
        long t2 = System.nanoTime();
        System.out.printf("BAD  (+=):            %,d ns, len=%d%n", t2 - t1, bad.length());

        // GOOD: O(n) — StringBuilder reuses buffer
        long t3 = System.nanoTime();
        StringBuilder sb = new StringBuilder(N); // pre-size!
        for (int i = 0; i < N; i++) {
            sb.append('x');
        }
        String good = sb.toString();
        long t4 = System.nanoTime();
        System.out.printf("GOOD (StringBuilder): %,d ns, len=%d%n", t4 - t3, good.length());

        // Modern alternatives for collections
        List<String> words = List.of("Java", "is", "great");

        // String.join
        String joined = String.join(", ", words);
        System.out.println("join:          " + joined);

        // Collectors.joining
        String streamed = words.stream().collect(Collectors.joining(", ", "[", "]"));
        System.out.println("joining:       " + streamed);

        // String.repeat (Java 11+): no loop needed
        String repeated = "ab".repeat(5);
        System.out.println("repeat:        " + repeated); // ababababab

        // Simple compile-time concat: JVM optimises this
        String name = "Java";
        String msg  = "Hello, " + name + "!"; // NOT in a loop — compiler/JVM can optimise
        System.out.println("simple concat: " + msg);
    }
}

Sources