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.
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
- https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuilder.html ā StringBuilder API
- https://openjdk.org/jeps/280 ā JEP 280: invokedynamic string concatenation (Java 9)