Skip to main content

Why String += in Loops Kills Your Code (O(NΒ²) Copy Trap)

🎯 The Question​

"In Java, Python, and JavaScript, appending strings inside a loop using s += item; appears to be an innocent O(N)O(N) loop. Why does concatenating 100,000 strings this way cause application execution to grind to a halt and consume gigabytes of garbage-collected RAM?"


⚑ 30-Second Elevator Pitch​

In high-level languages (Java, Python, JavaScript, C#), Strings are strictly Immutable:

  • Once created in memory, a string's underlying character array can never be modified in-place.
  • When you write s += item, the runtime does not append characters to the existing memory buffer.
  • Instead, it:
    1. Allocates a brand-new string object on the heap of size len(s)+len(item)\text{len}(s) + \text{len}(item).
    2. Copies all existing characters of ss into the new buffer.
    3. Copies the new itemitem characters.
    4. Abandons the old string object to be collected by Garbage Collection.

If a loop runs NN times, iteration ii copies ii characters. Total work performed is: 1+2+3+β‹―+N=N(N+1)2=O(N2)1 + 2 + 3 + \dots + N = \frac{N(N + 1)}{2} = \mathbf{O(N^2)}

Concatenating 100,000 strings copies over 5 billion characters and generates millions of temporary heap garbage objects!


🧠 Under-the-Hood: O(NΒ²) Repeated Memory Allocations​


πŸ”¬ Production Solution: Mutable Buffers and Array Joins​

Java: StringBuilder​

// ❌ DANGEROUS: O(N^2) time & memory
String result = "";
for (String s : list) { result += s; }

// βœ… PRODUCTION: O(N) linear time
StringBuilder sb = new StringBuilder();
for (String s : list) { sb.append(s); }
String result = sb.toString();

Python: ''.join(list)​

# ❌ DANGEROUS: Repeated string recreation
res = ""
for w in words: res += w

# βœ… PRODUCTION: Single pass memory allocation
res = "".join(words)

Python's ''.join() calculates the total length of all strings in a single initial pass, allocates exactly one memory buffer of the precise total size, and copies each string into place in O(N)O(N) linear time.


πŸ“Œ Comparison Matrix: String += vs. StringBuilder / Join​

MetricString += (Immutable Loop)StringBuilder / ''.join()
Time Complexity🐒 O(N2)O(N^2) Quadratic⚑ O(N)O(N) Linear
Heap AllocationsNN separate string objects1 internal resizable buffer + 1 final string
Garbage Collector LoadSevere (Triggers frequent GC stop-the-world pauses)Minimal (Near zero heap garbage)
100,000 Iterations~15 to 45 seconds~4 milliseconds (10,000Γ—10{,}000\times faster!)

πŸ’‘ What Interviewers Ask Next (Follow-Up Traps)​

  1. "Doesn't the modern Java compiler automatically optimize + to StringBuilder?"

    • Answer: Only for single-line expressions, like String s = a + b + c;. The compiler replaces this single line with new StringBuilder().append(a).append(b).append(c).toString(). However, inside a loop, the compiler creates a brand-new StringBuilder on every single iteration, completely failing to solve the O(N2)O(N^2) re-allocation problem!
  2. "What is the difference between StringBuilder and StringBuffer in Java?"

    • Answer: StringBuffer is synchronized (thread-safe) using method-level locks, introducing mutex locking overhead on every call. StringBuilder is unsynchronized (not thread-safe), making it significantly faster for local loop operations.

Placement & Interview Takeaway

Interview Answer: String concatenation in loops creates O(N2)O(N^2) complexity because strings are immutable. Each += allocates a new memory buffer and re-copies all previous characters. Always use mutable buffers like Java's StringBuilder or Python's ''.join() to append characters in O(N)O(N) linear time.


πŸ“Ί Video Explanation​

πŸ’¬

Discussion & Doubts