Two words on one page

Collisions

The analogy

Sometimes two different words land on the same page. You do not panic โ€” you write both on that page and read the short list to see which one you wanted. Collisions are normal; you just need the lists to stay short.

Visualizer

Two words on one page

step 1 / 19
[0]
[1]
[2]
[3]
[4]

Only 5 pages this time, to force the problem into the open.0 entries / 5 pages โ€” load factor 0.00

๐Ÿ“– In depth โ€” the full reference

The two collision strategies

strategyideadeletememoryused by
separate chainingeach bucket holds a little list; colliders line up in iteasy โ€” unlink from the chainpointers per entryJava HashMap, C++ unordered_map
open addressingbucket taken? probe another slot in a fixed patterntricky โ€” needs tombstonesone flat array, cache-friendlyCPython dict, V8, Rust HashMap

Load factor โ€” the crowding dial

load factor = entries / buckets. Low: wasted space but rare collisions. High: compact but chains grow and O(1) rots toward O(n). Every real implementation picks a threshold and RESIZES past it, rehashing every key into a bigger table โ€” an O(n) event amortized across the inserts that caused it. This is the dynamic-array growth story again, wearing a hash.

CPython dict internals

  • Open addressing with a pseudo-random probe sequence (perturb): a collision does not check the NEXT slot โ€” it jumps in a pattern derived from the full hash, dodging pile-ups.
  • Resizes when 2/3 full โ€” the table you pay for is always โ‰ฅ1.5ร— your entries.
  • Small ints hash to themselves: hash(42) == 42. Strings use SipHash, randomized per process.
  • Two objects that compare equal MUST hash equal โ€” that is why you always override __hash__ together with __eq__.
  • A malicious set of all-colliding keys turns a dict into an O(nยฒ) denial-of-service โ€” the reason string hashing is randomized (a real 2011 attack on web frameworks).
buckets = [[] for _ in range(5)]

def put(k, v):
    i = my_hash(k, len(buckets))
    buckets[i].append((k, v))   # chain, do not overwrite

def get(k):
    i = my_hash(k, len(buckets))
    for key, val in buckets[i]:  # scan the short chain
        if key == k:
            return val
Check yourself

Two keys hash to bucket 3. The mapโ€ฆ

Practice โ€” write it yourself

group_by_bucket(keys, n) hashes every key with the same ord-sum % n and returns a list of n buckets (lists) โ€” collisions chain together.

Python 3 ยท runs in your browser ยท your draft is saved locally
๐Ÿ“ My notessaved in this browser