A hash table lets you find something by name in about one step, however many things are stored. That sounds like magic, and the trick behind it is surprisingly simple. This part uses a coat-check picture, an animation with a collision, and an Expert view on the details that make hash tables work and sometimes fail.
The coat-check picture
At a coat check, you hand over a coat and get a numbered ticket. Later the ticket tells staff exactly which hook to go to, with no searching along the rail.
Expert viewDefinitions
A hash table stores key-value pairs in an array of buckets. A hash function maps a key to an integer; the table reduces that integer to a valid bucket index, often by taking the remainder after dividing by the number of buckets. A good function is deterministic (the same key always gives the same number) and spreads keys evenly. Lookup, insert and delete all start by hashing the key.
Watch it work
Show the 8 steps as text
- A hash table has numbered buckets. A hash function turns each key into a bucket number.
- "ann" maps to bucket 2.
- "bob" also maps to bucket 2: a collision, so it is chained after "ann".
- "cat" also maps to bucket 2: a collision, so it is chained after "ann".
- "dan" also maps to bucket 2: a collision, so it is chained after "ann".
- "eve" maps to bucket 0.
- "fay" also maps to bucket 0: a collision, so it is chained after "eve".
- To look a key up, hash it again and check only that one bucket.
Expert viewWhy collisions are unavoidable
There are far more possible keys than buckets, so by the pigeonhole principle some keys must share a bucket. Even with few keys, collisions appear sooner than intuition suggests: the birthday problem shows that among only 23 people, two share a birthday with probability a little over one half. So a design must assume collisions and handle them efficiently.
- 1Take the keyFor example the word "cat"
- 2Hash itThe hash function gives a number
- 3Pick the bucketReduce the number to a bucket index
- 4Compare keysCheck the items in that bucket against the key
- 5Found or notA match returns the value; no match means the key is absent
Takeaway. Hashing jumps straight to one bucket, and only the few items there are compared.
Handling collisions
Chaining
- Each bucket holds a small list of items
- A collision just adds to the list
Open addressing
- Items live in the array itself
- On a collision, try the next free slot by a fixed rule
Expert viewChaining and open addressing compared
With chaining, each bucket stores a collection (often a linked list), and a lookup compares keys along that chain. With open addressing, every item sits directly in the array; a collision triggers probing, such as stepping to the next slot (linear probing) or following another deterministic sequence. Deleting under open addressing needs care, because simply emptying a slot can break the probe path of other items, so a marker is usually left behind. Open addressing keeps data compact, but its performance degrades sharply as the table fills.
Load factor and resizing
- Count the itemsCompare with the number of buckets
- Check the load factorItems divided by buckets
- Too full? Make more bucketsOften double the size
- Re-hash every itemEach one gets a new bucket in the bigger table
Expert viewLoad factor and amortised cost
The load factor is the number of stored items divided by the number of buckets. Tables keep it below a chosen limit by growing when it is exceeded. Growing means creating a larger array and re-inserting every item, which is O(n) for that one insert. If the size grows by a constant factor such as doubling, the cost spread over all the inserts is constant per insert, so inserts are amortised O(1). Where a table must never pause, some designs spread the re-hashing over many operations.
What it costs
| Operation | Average case | Worst case |
|---|---|---|
| Look up a key | O(1) | O(n) |
| Insert | O(1) amortised | O(n) |
| Delete | O(1) | O(n) |
| Extra space | O(n) | O(n) |
- about 1bucket checked on average
- up to 1,000,000items checked in the worst case
Expert viewAverage versus worst case
The O(1) average assumes a good hash function and a bounded load factor, so each bucket holds only a few items. The worst case is O(n): if every key lands in one bucket, a lookup is a linear scan of everything. (Some designs reduce the chain cost, for example by storing long chains in a balanced structure, but that is an implementation choice and not part of the basic idea.) Hash tables also do not keep keys in sorted order, so "give me the smallest key" or "all keys from A to F" is not what they are for.
Where hashing is used
- Dictionaries and mapsLook up a value by key: names to phone numbers
- Sets"Have I seen this before?" in about one step
- CachesStore recent results by key so repeats are instant
- Spotting duplicatesHash each record, then compare only those that share a bucket
- Counting wordsOne counter per word, found by hashing the word
Hashing also appears in checksums: a fast hash of a file or message is recomputed later to check that nothing was corrupted by accident.
A different job: passwords
- User to Server The user chooses a password and sends it over a protected connection
- Server (to itself) The server adds a unique random salt for this user
- Server (to itself) It runs a deliberately slow password-hashing function
- Server (to itself) It stores the salt and the hash, and forgets the password
- User to Server Later the user types the password again
- Server (to itself) The server hashes the typed password with the stored salt
- Server to User (reply) If the two hashes match, access is allowed
Takeaway. The server checks a password by comparing hashes, so it never needs to read the password back.
Hash table hash
- As fast as possible
- Spreads keys evenly across buckets
Password hash
- Deliberately slow, so guessing is costly
- Salted per user so equal passwords look different
Expert viewGood hash functions, and which kind to use
For tables, a good function is deterministic, fast, and uniform: each bucket should be about equally likely, and small changes in a key should scatter the results. It must also agree with equality: keys that are equal must give equal hashes.
Non-cryptographic hashes are built for speed and spread, and suit tables and checksums. Cryptographic hashes add security goals, such as being infeasible to reverse or to find two inputs with the same output. Password storage needs more again: a hash that is slow and expensive to compute, designed to resist guessing at scale, plus a salt to defeat precomputed lists. A fast cryptographic hash is still the wrong tool for passwords.
Expert viewHash-flooding attacks
If an attacker knows the hash function and can choose the keys (for example, names in a web request), they can pick many keys that all collide, forcing the table into its O(n) worst case and making each operation slow. Common defences in general terms: use a hash function with a secret random seed chosen at start-up, limit input sizes, or use a table design that bounds the damage of long chains. This is one more reason not to invent your own hash for untrusted input.
Try it by hand
- Draw 5 buckets, numbered 0 to 4Your hooks
- Give each letter a numberA is 1, B is 2, and so on
- Hash a wordAdd its letter numbers, divide by 5, keep the remainder
- Place the word in that bucketWrite it on the hook
- Try CAT, ACT and TACThey share letters, so they collide: how do you handle it?
A hash table trades a little memory and a good hash function for lookups in about one step.
Frequently asked questions
Is a hash table always O(1)?
Only on average, with a good hash function and a controlled load factor. The worst case is O(n).
Can two different keys have the same hash?
Yes. That is a collision, and tables are designed to cope with it.
Can I get the original key back from the hash?
A table does not need to, because it stores the key. For security hashes, being hard to reverse is a design goal.
Can I store passwords with a fast hash and a salt?
No. Use a dedicated slow password-hashing function from a vetted library.