Efficient Key-Value Storage Using Hash Tables

A hash table maps keys to values using a computed index, enabling fast lookup, insertion, and deletion. Understanding arrays and linked lists is essential before working with hash tables.

Core Mechanism

An array serves as the underlying storage, where each slot—called a bucket—holds a key-value pair. A hash function processes the key, and the result is combined with the array length via a modulo operation to produce the target bucket index. This index directly yields the memory location of the bucket, allowing immediate access to the stored data.

def compute_index(key, capacity):
    h = hash_function(key)
    return h % capacity

Collision Scenarios

Hash collisions occur when distinct keys yield the same bucket index after hashing and modulo operations. Multiple entries then contend for the same slot.

Collision Resolution Strategies

Bucket-Linked Lists

When a collision happens, the new entry is appended to a linked list inside the occupied bucket. Lookup requires traversing this list to match the desired key. Traversal makes this approach slower for heavily loaded buckets.

class BucketNode:
    def __init__(self, k, v):
        self.key = k
        self.val = v
        self.next = None

class HashTable:
    def __init__(self, size):
        self.cap = size
        self.store = [None] * size

    def insert(self, k, v):
        idx = compute_index(k, self.cap)
        node = self.store[idx]
        if not node:
            self.store[idx] = BucketNode(k, v)
            return
        while node:
            if node.key == k:
                node.val = v
                return
            if not node.next:
                break
            node = node.next
        node.next = BucketNode(k, v)

Dynamic Resizing

Increasing the number of buckets reduces the probability of collisions because the modulo result changes for existing keys. This process rebuilds the table with a larger array and reinserts all entries—known as rehashing. Since every key’s index may differ, performance overhead is significant.

def resize(table, new_cap):
    old_store = table.store
    table.cap = new_cap
    table.store = [None] * new_cap
    for head in old_store:
        curr = head
        while curr:
            table.insert(curr.key, curr.val)
            curr = curr.next

In parctice, systems often combine both strategies: initial collisions are handled with linked lists per bucket, and once a list exceeds a threshold length, resizing is triggered. To mitigate rehashing cost, migration can be performed incrementally during normal operations, retaining the old bucket array until relocation completes.

Tags: Hash Table Data Structures collision resolution Hashing Key-Value Store

Posted on Mon, 21 Sep 2026 16:29:03 +0000 by simon551