What happens to a hash-table entry when the key object's fields change after insertion?
answer
- ask who would recompute the bucket
- the table is never notified
- insert-time bucket versus lookup-time bucket
- old value placed it, new value searches
- present in the table, unreachable by lookup
basics
~20 sNothing moves. The entry stays in the bucket chosen from the key's old hash value, while later lookups hash the new field values and probe elsewhere. The entry is stranded: unreachable by lookup, still occupying the table.
solid answer
~40 sNothing in the table reacts, because nothing tells it anything. A hash table computes a bucket index at exactly two moments: when an entry is inserted, and when a probe key is looked up. Mutating a key's identifying fields after insertion changes what future probes hash to, but no one re-places the entry already stored. It sits in the bucket chosen from its old values, invisible to any lookup built from the new ones — while still counting toward size and still appearing during iteration. Re-inserting the same logical key then adds a *second* entry that compares equal to the first. Nothing throws; the table quietly answers "not present" for something it is holding. The rule that follows: only fields that never change may feed a key's hash or equality.
code
pseudocode · 12 lines// insert
b = hash(key) mod length(buckets)
add_to_chain(buckets[b], key, value)
// ... some code mutates key's identifying fields; the table is not told ...
// lookup, later, with a probe holding the new values
b = hash(probe) mod length(buckets)
for entry in buckets[b]:
if entry.key == probe:
return entry.value
return NOT_FOUNDgo deeper
Be ready to say what actually happens: the entry stays in the bucket chosen at insert, and a lookup built from the new field values probes a different bucket and misses. No error is raised.
Explain the two moments a bucket index is computed — insert and lookup — and why nothing in between re-places a stored entry. Describe the symptom set: failed lookups, size growing past the distinct count, entries visible only through iteration.
Show how you would recognise this in production: a cache hit rate collapsing right after a normalization step shipped, work reprocessed although it was marked done, a set larger than the number of distinct items. Then fix it at the key type, not at the call site.
Own it as a rule the codebase enforces: anything used as a key has frozen identifying state, and the boundary where foreign objects become keys is where that is guaranteed. Weigh the copy that costs against the class of silent bug it removes.
## The promise a hash table makes A hash table stores an entry by turning its key into a number and using that number to pick a slot: `bucket = hash(key) mod capacity`. Lookup repeats the same arithmetic on the probe key, walks only that one bucket, and confirms a match with an equality comparison. That is the whole trick, and it is why lookup is expected O(1) rather than O(n) — the table never searches the other buckets. The trick rests on one silent assumption: **the number a key produces today is the number it produced when it was stored.** Nothing in the machinery checks that assumption. ## The two moments a bucket index is computed There are exactly two: insert and lookup. There is no third moment where the table walks its contents asking whether each key still hashes to the bucket it lives in — that would cost O(n) and defeat the point of the structure. A key object is not a broadcast source either: mutating one of its fields fires no notification, sets no dirty bit, and reaches no data structure that happens to be holding it. So when a key mutates after insertion, the entry does not move, and it does not disappear. It becomes **stranded**: physically present in a bucket that no longer corresponds to its own value. ## What it looks like when it bites A route-planning tool keeps a visited-set of waypoint objects keyed by latitude and longitude, so that a waypoint already processed is skipped. Later someone adds a normalization pass that snaps coordinates onto a grid and rewrites them *in place*. Every waypoint recorded before the pass is now stranded. The symptoms an engineer actually sees: - **Lookups miss.** `contains(waypoint)` answers false for a waypoint the set is holding, so the tool reprocesses work it already did. - **Size grows past the number of distinct items.** Re-adding the "missing" waypoint hashes to the new bucket, finds nothing there, and stores a second entry. The set now holds two elements that compare equal to each other — a state the structure is supposed to make impossible. - **Iteration still shows everything.** Iteration walks buckets or slots directly and never hashes anything, so the stranded entry is right there in the dump. That contrast — "I can see it when I print the table, but `contains` says no" — is the diagnostic fingerprint of a mutated key. - **Nothing throws.** No exception, no warning, no corrupted memory. The table's own invariants are intact; it is the relationship between the table and the key that broke. ## Does anything heal it? A common hope is that the next growth-and-rehash pass fixes things. It depends on a degree of freedom implementations differ on: a table that **stores each entry's hash alongside the entry** (a very common optimization, since it makes rehashing and equality short-circuiting cheap) will re-place the entry using the stale stored number and keep it exactly as stranded. A table that **recomputes the hash from the key during resize** will relocate the entry to a bucket consistent with its current values — at which point it may silently become findable again, and any duplicate you inserted meanwhile becomes visible as a real duplicate. Behaviour that changes depending on how full the table happens to be is worse than a consistent failure, not better. Never rely on a rehash to repair a mutated key. ## Which mutations are actually dangerous Only fields that participate in the key's hash or its equality comparison. A waypoint's `label` or `lastViewedAt`, if neither the hash nor equality reads them, can change freely while the object sits in a table — the placement stays correct and probes still match. This is what makes the bug so slippery: the same object is a perfectly safe key until someone adds one field to the hash, or mutates one field that was already in it. Note also that the danger runs the other way for equality-only fields. If equality reads a field the hash ignores, mutation does not strand the entry, but a probe that used to match may stop matching inside the right bucket — the lookup still fails, just for the other half of the contract. ## The rule to state out loud **A key's identifying state must be frozen for as long as it is a key.** Either the type cannot change (identifying fields set once, never rewritten), or you copy those fields into something that cannot change before handing it to the table. Anything else — a comment, a code-review convention, an intention not to mutate — is not a defense, because the failure it prevents is silent and shows up far from the mutation that caused it.
- If lookups miss the stranded entry, why does iterating the table still list it?Iteration walks the buckets or slots in storage order and never hashes anything — it just yields whatever is physically there. Only lookup depends on the hash matching the placement. That asymmetry is the giveaway: an entry you can see in a full dump but cannot retrieve by key is almost always a mutated key.
- What happens if you insert the same logical key again after mutating it?The insert hashes the current values, lands in a different bucket, finds nothing equal there, and stores a second entry. The table now holds two entries whose keys compare equal, and size has grown by one. In a set this shows up as a duplicate the structure was supposed to make impossible.
- Is it always a problem to mutate an object that is being used as a key?Only if the mutated field feeds the hash or the equality comparison. Changing a display label that neither reads is harmless. That is exactly why this bug appears long after the code was written: someone adds one field to the hash, and a previously safe mutation becomes a silent stranding.
It is a book reshelved by title, whose title is then changed with a marker. The book is still on the shelf; the catalogue sends you to the wrong aisle.
saying these in an interview costs you the question
- Claims the table rehashes the key when it changes
- Says the entry is dropped from the table on mutation
- Expects the next resize to repair the stale placement
- Thinks the equality check runs before the bucket is chosen
- Believes iteration also skips the stranded entry