Hash Table: Computer Science Study Notes
October 10, 2026
📚 Comprehensive Guide to Hash Tables
- Core Concepts & Overview: Associative arrays, hash functions, and load factor mechanics
- Hash Function Design: Integer universe assumptions, specific algorithms, and uniformity requirements
- Collision Resolution Strategies: Separate chaining vs. open addressing and advanced variants
- Dynamic Resizing: Full rehashing, incremental approaches, and linear hashing
- Performance Analysis: Time complexities, distribution impacts, and statistical expectations
- Practical Applications: Database indexing, caching, sets, and transposition tables
- Language Implementations: Built-in hash table support across popular programming languages
💡 Overview of Hash Tables
In computer science, a hash table is a fundamental data structure that implements an associative array (also called a dictionary or map). An associative array is an abstract data type that maps keys to values.
- Key-Value Mapping: An array of length stores elements, where .
- Hash Indices: A hash table uses a hash function to compute an index (or hash code) , where .
- Storage Strategy: Both the key and its associated value are stored at the computed index. Storing the key alongside the value ensures that lookups can verify the key to retrieve the correct value, even when collisions occur.
- Hash Maps: A map implemented via a hash table is commonly referred to as a hash map.
[Key] ---> [ Hash Function ] ---> [ Hash Code / Index ] ---> [ Bucket Array A[h(x)] ]
The Space-Time Tradeoff
Hashing is a classic example of a space–time tradeoff:
- Infinite Memory: The entire key can be used directly as an index to locate its value with a single memory access.
- Infinite Time: Values can be stored without regard for their keys, relying on a binary search or linear search to retrieve elements.
⚖️ Load Factor and Performance
The efficiency of a hash table heavily depends on its load factor (), defined as the ratio of stored elements to available slots:
Where:
- = number of key-value pairs in the hash table
- = number of buckets
Statistical Distribution
In the limit of large and , each bucket statistically follows a Poisson distribution with expectation for an ideally random hash function.
Maximum Load Factors by Resolution Strategy
| Strategy | Maximum Load Factor () Behavior | Recommended Range |
|---|---|---|
| Open Addressing | Cannot exceed because each slot holds exactly one item. Performance degrades severely as . | to |
| Separate Chaining | Performance declines gradually rather than sharply. No fixed threshold requires absolute resizing at . | to |
🧮 Hash Functions
A hash function maps the universe of keys to indices or slots within the table.
- Integer Universe Assumption: Conventional implementations assume all elements stem from a universe , where the bit length of fits within the architecture's word size.
- Perfect Hash Function: A hash function is perfect for a given set if it is injective on (each element maps to a distinct value). This is achievable when all keys are known in advance.
Common Hashing Schemes
1. Hashing by Division
The most commonly used scheme: where is the hash value and is the table size.
2. Hashing by Multiplication
where is a non-integer real-valued constant.
- Advantage: The choice of table size is not critical.
- Recommendation: Donald Knuth suggests using the golden ratio for constant .
3. String Hashing
- C++ Style: An unsigned integer (initially zero) is repeatedly left-shifted by one bit and XORed with the integer value of the next character, then modulated by table size.
- Polynomial Rolling Hash: Another widespread method for hashing strings into integers.
Choosing a Hash Function
- Uniform Distribution: Fundamental requirement. Non-uniform distributions increase collisions and resolution costs. Evaluate using statistical tests like Pearson's chi-squared test.
- Run Avoidance: For open addressing, functions must avoid runs (mapping multiple keys to consecutive slots), which destroys lookup performance. Multiplicative hashes can exhibit particularly poor run behavior.
- -Independent Hashing: Proves that a hash function does not feature bad keysets for specific table types, helping isolate the fastest possible function.
⚔️ Collision Resolution
Because most hash designs employ imperfect hash functions, hash collisions (where multiple keys generate the same index) must be accommodated.
1. Separate Chaining
In separate chaining, collided items are stored together using a linked list or alternative data structure at each array index.
Core Operations (Pseudocode)
Chained-Hash-Insert(T, x)
insert x at the head of linked list T[h(k)]
Chained-Hash-Search(T, k)
search for an element with key k in linked list T[h(k)]
Chained-Hash-Delete(T, x)
delete x from the linked list T[h(k)]
Advanced Chaining Data Structures
- Self-Organizing Trees: Using self-balancing binary search trees for ordered keys brings the theoretical worst-case down to .
- Dynamic Perfect Hashing: Employs two-level hash tables to guarantee worst-case lookup time. Buckets of entries are organized as perfect hash tables with slots. Array-based separate chaining can be up to 97% more performant than standard linked lists under heavy loads.
- Cache-Conscious Variants: Replacing linked lists with contiguous dynamic arrays exploits hardware-cache prefetchers (like TLBs), drastically reducing access time and memory consumption.
2. Open Addressing
In open addressing, every record is stored directly within the bucket array. Probing sequences determine alternative slots when a collision occurs.
Well-Known Probing Sequences
- Linear Probing: Fixed interval between probes (usually ). Offers high CPU cache utilization due to spatial locality.
- Quadratic Probing: Probing intervals are increased by adding successive outputs of a quadratic polynomial.
- Double Probing / Double Hashing: The probe interval is computed using a secondary hash function.
Open Addressing Variants
Coalesced Hashing
- A hybrid of separate chaining and open addressing. Colliding elements are placed in the largest-indexed available slot and linked to the original bucket via a
nextpointer. Ideal for fixed memory allocation.
Cuckoo Hashing
- Maintains two hash tables, each with its own hash function.
- If a slot is occupied, the resident item is displaced into the alternate table.
- Guarantees worst-case lookup complexity. If an infinite loop is detected via a threshold counter, both tables are rehashed with new functions.
Hopscotch Hashing
- Combines cuckoo hashing, linear probing, and separate chaining using the concept of a neighborhood of buckets (virtual buckets).
- Optimized for load factors exceeding and concurrent settings.
- Utilizes an H-bit bit array ("hop-information") in each bucket to indicate the relative distance of items originally hashed to that virtual bucket.
Robin Hood Hashing
- Favors displacing the element with the longest probe sequence length (PSL) from its home location.
- Dramatically reduces the variance of item distribution and long run formations.
- Nodes are augmented with an extra
pslvalue. During insertion, if the incoming item has a strictly greater PSL than the current table item, they are swapped and probing continues.
🔄 Dynamic Resizing
To maintain amortized performance as the load factor grows, hash tables undergo dynamic resizing and rehashing. Conversely, tables may shrink if they become too empty.
Resizing Strategies
| Strategy | Mechanism | Tradeoffs |
|---|---|---|
| All-at-Once Rehashing | Allocates a new table twice the size of the original and moves every item by recomputing hash values. | Simple to implement, but computationally expensive and unsuitable for real-time systems. |
| Incremental Rehashing | Gradually cleans buckets using wrapper commands (Add, Get, Delete) alongside two hash functions ( and ). | Avoids storage spikes, latency interruptions, and memory fragmentation. |
| Linear Hashing | Grows or shrinks the hash table dynamically one bucket at a time. | Provides smooth, incremental capacity adjustments. |
📈 Performance Summary
- Expected Time with Chaining (assuming uniform distribution):
- Successful search:
- Unsuccessful search:
- Core Dependency: Hash table performance relies entirely on the hash function's ability to disperse indices uniformly and prevent clustering.
🌐 Practical Applications
- Associative Arrays: Implementing in-memory dictionaries and maps.
- Database Indexing: Disk-based data structures (e.g.,
dbm), though B-trees remain more common. - Caches: Speeding up access to slower storage media. Collisions are typically handled by discarding/overwriting the older item.
- Sets: Storing unique values without order to test membership by omitting stored values and tracking key presence only.
- Transposition Tables: Storing previously evaluated positions in game search trees.
💻 Language Implementations
Many modern programming languages provide built-in hash table abstractions:
- JavaScript:
Objectcollections map string/symbol keys to values (primitive keys coerced to strings). ECMAScript 2015 introduced theMapstructure for arbitrary keys. - C++:
std::unordered_map(C++11) stores arbitrary key-value pairs in standard libraries. - Go: Built-in
maptype implemented via hash tables. - Java: Generic collections including
HashMap,HashSet,LinkedHashMap, andLinkedHashSet. - Python: Built-in
dicttype implements high-performance hash tables. - Ruby: Built-in
Hashutilizing open addressing models (since Ruby 2.4). - Rust:
HashMapandHashSetincluded in the standard library. - .NET (C#, VB.NET): Standard library provides
DictionaryandHashSet.