Friday, November 28, 2025

Interview Question : Implement Custom HashMap

Below is the best Java interview-level explanation + full custom HashMap implementation written in clean, production-style code.

I will give you:

  1. Concept explanation (what interviewer expects)

  2. Key points to mention verbally

  3. Full working implementation of CustomHashMap<K, V>

  4. Example usage


✅ 1. Concept Interviewers Expect

A custom HashMap usually demonstrates:

  • Array of buckets

  • Each bucket stores linked list (or tree – but interview usually accepts list)

  • put(key, value)

  • get(key)

  • remove(key) (optional but good)

  • Handling hash collision

  • Handling resize / rehash

You do NOT need to implement TreeNodes like JDK HashMap unless asked.


✅ 2. Key points to say in an interview

If asked verbally:

“My custom HashMap uses an array of buckets.
Each bucket holds a linked list of entries.
Key is converted to index by (hash & (capacity - 1)).
Collision is handled using chaining.
When load factor exceeds threshold, I resize the array and rehash all entries.”

This is enough to pass.


✅ 3. Full Implementation — CustomHashMap<K, V>

✔ Clean

✔ Easy to explain

✔ Close to how JDK HashMap works

public class CustomHashMap<K, V> {

    // Node inside each bucket (LinkedList)
    static class Entry<K, V> {
        K key;
        V value;
        Entry<K, V> next;

        Entry(K key, V value, Entry<K, V> next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    private Entry<K, V>[] table;      // bucket array
    private int capacity = 16;        // default size
    private float loadFactor = 0.75f;
    private int size = 0;             // current number of entries

    public CustomHashMap() {
        table = new Entry[capacity];
    }

    private int hash(K key) {
        return key == null ? 0 : (key.hashCode() & (capacity - 1));
    }

    public void put(K key, V value) {
        int index = hash(key);
        Entry<K, V> head = table[index];

        // Check if key already exists -> update it
        Entry<K, V> curr = head;
        while (curr != null) {
            if ((key == null && curr.key == null) || (key != null && key.equals(curr.key))) {
                curr.value = value;  // update
                return;
            }
            curr = curr.next;
        }

        // Insert new entry at the head of list
        Entry<K, V> newEntry = new Entry<>(key, value, head);
        table[index] = newEntry;
        size++;

        // Resize check
        if ((1.0 * size) / capacity >= loadFactor) {
            resize();
        }
    }

    public V get(K key) {
        int index = hash(key);
        Entry<K, V> curr = table[index];

        while (curr != null) {
            if ((key == null && curr.key == null) || (key != null && key.equals(curr.key))) {
                return curr.value;
            }
            curr = curr.next;
        }

        return null; // not found
    }

    public V remove(K key) {
        int index = hash(key);
        Entry<K, V> curr = table[index];
        Entry<K, V> prev = null;

        while (curr != null) {
            if ((key == null && curr.key == null) || (key != null && key.equals(curr.key))) {

                // delete node
                if (prev == null) {
                    table[index] = curr.next;
                } else {
                    prev.next = curr.next;
                }

                size--;
                return curr.value;
            }

            prev = curr;
            curr = curr.next;
        }
        return null;
    }

    // Resize: Double capacity and rehash all entries
    private void resize() {
        int oldCapacity = capacity;
        capacity = capacity * 2;

        Entry<K, V>[] oldTable = table;
        table = new Entry[capacity];
        size = 0;

        for (int i = 0; i < oldCapacity; i++) {
            Entry<K, V> entry = oldTable[i];
            while (entry != null) {
                put(entry.key, entry.value); // rehash
                entry = entry.next;
            }
        }
    }

    public int size() {
        return size;
    }
}

✅ 4. Example Usage

public class Main {
    public static void main(String[] args) {
        CustomHashMap<String, Integer> map = new CustomHashMap<>();

        map.put("A", 1);
        map.put("B", 2);
        map.put("C", 3);

        System.out.println(map.get("B")); // 2

        map.put("B", 20); 
        System.out.println(map.get("B")); // 20

        map.remove("A");

        System.out.println(map.size()); // 2
    }
}

Want an even more advanced version?

I can also give you:

  • Open addressing (linear probing) HashMap

  • HashMap with TreeBins like JDK8

  • ConcurrentHashMap simplified version

  • Memory-efficient hash map with primitive arrays (Agrona-style)

Tell me which version you'd want next.

No comments:

Post a Comment

CompletableFuture

  Welcome back to  our concurrency series ! In our first discussion, we likely touched on the traditional models of threading. Today, we’re ...