juniorStrings

Why are strings immutable in many languages?

Updated Feb 20, 2026

Short answer

Immutable strings can be shared freely without defensive copying, cached safely as hash-map keys, and passed between threads without synchronisation — because nothing can change them after creation. The trade-off is that every modification allocates a new string, which makes concatenation in a loop quadratic unless you use a builder.

Deep explanation

Java, C#, Python, JavaScript, and Go all make strings immutable. The reasons compound:

  • Safe sharing. Any number of references can point at one string with no risk that one holder mutates it under another. Without this, every method receiving a string would need a defensive copy to be safe.
  • Hash caching. A string's hash can be computed once and cached, because it can never change. This is what makes strings efficient hash-map keys — and it is why a mutable key is dangerous: mutate it after insertion and the entry becomes unreachable in its original bucket.
  • Thread safety for free. Immutable data needs no locks. Multiple threads read the same string concurrently with no synchronisation and no data races.
  • Interning. Identical literals can share one instance, cutting memory in programs that manipulate many repeated strings.
  • Security. A file path or SQL fragment validated at a boundary cannot be altered afterwards by another reference — closing a whole class of time-of-check-to-time-of-use bugs.

The cost. Every "modification" allocates:

Python
# O(n²) — each += builds a whole new string
s = ''
for chunk in chunks: # 10,000 iterations
s += chunk # copies everything so far, every time
# O(n) — collect, then join once
s = ''.join(chunks)

At 10,000 chunks the first form copies roughly 50,000,000 characters; the second copies each character once. This is the single most common performance bug arising from string immutability, and every language provides the escape hatch: StringBuilder in Java and C#, strings.Builder in Go, ''.join() in Python.

CPython optimises some += cases when the string has exactly one reference, which makes the quadratic behaviour intermittent and therefore harder to spot.

Real-world example

Immutability is why this is safe:

Python
config = {'/etc/app.conf': parsed_settings} # string key, hash cached
path = '/etc/app.conf'
validate(path) # cannot be tampered with after validation
load(path) # guaranteed to be the same path validated above

And why a mutable key is not. In a language with mutable strings, another reference could rewrite path between validate and load, or mutate a key already stored in the map and orphan its entry — the dictionary would keep the old hash bucket while the key now hashes elsewhere.

Common mistakes

  • - Building strings with `+=` in a loop, which is O(n²)
  • use a builder or join.
  • - Assuming a method like `.upper()` or `.strip()` mutates in place — they return a new string, and ignoring the return value is a silent no-op.
  • - Relying on `is` / reference equality for string comparison. Interning makes it work for literals and then fail for computed strings.
  • - Treating immutability as a guarantee of secrecy — the old value stays in memory until garbage collected, which is why sensitive data belongs in a mutable char array that can be zeroed.
  • - Assuming all languages agree
  • C strings and Ruby strings are mutable, and code ported between them can break in subtle ways.

Follow-up questions

  • Why does string concatenation in a loop become O(n²)?
  • What is string interning?
  • Why are passwords recommended to be held in char arrays rather than strings?
  • How does immutability help concurrency?

More Strings interview questions

View all →