skip to content

What does it mean for a string to be immutable, and what does immutability buy you?

level: juniorimportance: must knowfreq 74%

answer

  1. separate the variable from the value
  2. who else is holding this value right now
  3. what can you cache if contents never drift
  4. the defensive copies you no longer need
  5. the price is paid on every edit

basics

~20 s

Immutable means an existing string's characters never change: operations that look like edits return a new string. Because the value cannot change under anyone holding it, it can be shared without defensive copies, hashed once, and read concurrently.

solid answer

~50 s

Immutability is a property of the **value**, not of the variable. A variable can be pointed at a different string tomorrow; what cannot happen is that the characters inside a string someone else already holds change while they hold them. Three things follow directly. First, **safe sharing**: when you hand a string to another component you can pass the reference rather than a defensive copy, because no one can edit it behind your back. Second, **a hash computed once**: since the contents are fixed, a derived value such as a hash can be computed on first use and cached for the object's lifetime, which is why immutable values make such good lookup keys. Third, **thread safety without synchronization**: many readers can read the same characters concurrently with no lock, because there are no writers. The price: every edit allocates a fresh string.

go deeper

for a junior

Be ready to state plainly that the characters of an existing string never change and that edit-like operations return a new string. Give one concrete benefit, such as passing a string to another component without copying it first.

for a middle

Explain the mechanics behind each payoff: which defensive copies disappear, which derived values become cacheable, and why the cost of an edit is proportional to the string's length rather than to the size of the change.

for a senior

Show where the payoff is measurable in production — copies removed at module boundaries, hash work removed from hot lookups — and be honest about the allocation churn immutability creates and how you keep it out of hot loops.

for a principal

Own the default: when a codebase standardises on immutable values it trades allocation pressure for whole categories of aliasing bugs that never need review, and you should be able to argue when a specific hot path earns an exception and how that exception is contained.

## The claim, stated precisely A string value is **immutable** when the sequence of characters inside an already-created string can never be modified after creation. Every operation that appears to change a string — uppercasing, trimming, replacing, appending — actually **returns a new string** and leaves the original untouched. The single most common misunderstanding is to confuse the **value** with the **variable that names it**. These are different things: ``` s = "alpha" s = s + "-beta" // legal: s now names a NEW string, "alpha-beta" // the original "alpha" object was never modified ``` The assignment rebinds the name. Anyone else still holding a reference to the first string still sees `alpha`. Immutability constrains the object; it says nothing about how many times a variable may be reassigned. A separate notion — a read-only or constant *binding* — constrains the variable and says nothing about the object. Interviewers ask this because candidates who conflate the two give confident wrong answers about aliasing later. ## What immutability actually buys **1. Safe sharing without defensive copies.** With a mutable buffer, handing your data to another component is dangerous: it may keep the reference and read it later, after you have overwritten the bytes; or it may write into it while you read. The standard defence is to copy on the way in and on the way out — O(n) per hand-off, paid forever, on both sides of every boundary. An immutable value removes the hazard, so the copy can be removed too. In a pipeline that tags every event with a service name, the same characters can be referenced by the event, the batching layer and the metrics aggregator at once, with zero copies. **2. Derived values can be cached.** Anything computed *from* the contents stays correct forever, because the contents cannot drift. A hash value is the important case: compute it once on first use, store it beside the characters, and every later lookup is a field read plus, on a match, one content comparison. Length, an "is this pure ASCII" flag, and a precomputed search table are cacheable for the same reason. With a mutable buffer, every one of these must either be recomputed on each use or invalidated on each write. **3. Concurrency for free.** Data races require at least one writer. An immutable value has no writers after publication, so any number of threads may read it with no lock, no copy and no memory barrier beyond the one that published the reference in the first place. Note the exact scope of that promise: the **string** needs no synchronization; the **variable** holding it still does, because reassigning a shared variable is a write like any other. "My program is thread-safe because my strings are immutable" is wrong, and interviewers listen for it. **4. Values are usable as keys and as cache entries.** A key whose contents cannot change cannot silently move to a different bucket after insertion — the whole reason immutable values are the default recommendation for lookup keys and set members. ## What it costs Every edit allocates. A one-character change to an n-character string is O(n) time and O(n) new memory, because the result is a whole new string. That is fine for the occasional transformation and quietly catastrophic when applied repeatedly in a loop — which is why environments that make strings immutable also ship a mutable growable buffer for building them. There is also churn: many short-lived strings put pressure on whatever reclaims memory, which is exactly the pressure that tempts engineers into reusing one mutable buffer and then reintroduces the sharing hazards above. Mainstream ecosystems have split on this in an instructive way: Java, C# and Python make the string type immutable and provide a separate mutable builder, while C++ and Go's byte slices give you mutable character storage directly and leave the sharing discipline to you. The tradeoff is the same in every one of them — the question is only where the default sits. ## How to answer out loud Say what cannot change (the characters of an existing value), distinguish it from what can (which value a variable names), then give the three payoffs — share without copying, cache anything derived, read concurrently without locks — and close with the cost: every edit is a new allocation, so build strings with a buffer rather than by repeated edits.

  • If a string cannot change, why can I write s = s + "!" without an error?
    Because that statement does not modify a string; it builds a new one from the old characters plus `!` and then points the name `s` at the result. The original value is untouched, and anyone else still referencing it sees the old characters. Immutability constrains the object, not the binding.
  • What does immutability let a lookup structure cache about a key?
    Its hash value. Because the characters are fixed for the object's lifetime, the hash can be computed on first use and stored alongside the value, so repeated lookups of the same key skip the O(n) scan over the characters and do a field read plus one confirming content comparison on a bucket hit.
  • Does immutability make code that uses strings automatically thread-safe?
    No. It removes races on the characters themselves, so any number of threads may read one string with no lock. It does nothing about the variables and containers holding references: reassigning a shared field or inserting into a shared map is still a write that needs its own synchronization or an appropriate concurrent structure.

A printed page versus a whiteboard. You can hand the page to ten people and walk away; whatever they read later is what you wrote. Hand out the whiteboard and any of them may erase it while another is reading.

saying these in an interview costs you the question

  • Says immutable means the variable cannot be reassigned
  • Thinks immutable strings must be known at compile time
  • Claims a program is thread-safe just because its strings are
  • Believes an edit is rejected rather than returning a new value
  • Assumes immutability costs nothing because nothing is ever copied
  • Confuses immutability with equal values being one shared object

context