Advanced Java October 06 ,2025

Custom Implementation of LinkedHashMap in Java

Preserving Order with Efficiency

Introduction

The LinkedHashMap is a powerful Java collection that combines the fast lookup of a HashMap with the ordering of a linked list.

While a HashMap makes no guarantees about the order of elements, a LinkedHashMap preserves either insertion order or access order (depending on configuration).

This is particularly useful when order matters, for example:

  • Maintaining a cache with least-recently-used eviction.
  • Preserving configuration settings in the order they were loaded.

In this blog, we will:

  • Understand the internal structure of LinkedHashMap.
  • Learn about its ordering mechanism.
  • Build a custom LinkedHashMap step-by-step.
  • Compare it with HashMap.
  • Discuss pros, cons, and use cases.

Internal Working of LinkedHashMap

LinkedHashMap extends HashMap and adds a doubly-linked list running through all entries.
This allows it to maintain order while still having constant-time performance for operations.

Structure

Hash Table (array of buckets) + Doubly Linked List

Each entry contains:

  1. Key
  2. Value
  3. Hash code
  4. Next (for bucket chaining)
  5. Before and After pointers (for linked list order)

This linked list connects entries in the order they were inserted (or accessed).

Two Ordering Modes

  • Insertion order — entries remain in the order they were inserted. (Default mode)
  • Access order — entries are moved to the end of the list when accessed.

Designing a Custom LinkedHashMap

We will implement a simplified version of LinkedHashMap that preserves insertion order.

Step 1: Define the Entry Class

This will hold the key-value pair along with pointers for linked list order.

class LinkedEntry {
    K key;
    V value;
    LinkedEntry next; // for bucket chaining
    LinkedEntry before; // previous entry in linked list
    LinkedEntry after; // next entry in linked list

    public LinkedEntry(K key, V value, LinkedEntry next) {
        this.key = key;
        this.value = value;
        this.next = next;
    }
}

Step 2: Create the CustomLinkedHashMap Class

public class CustomLinkedHashMap {
    private int capacity = 16;
    private LinkedEntry[] table;
    private LinkedEntry head; // start of linked list
    private LinkedEntry tail; // end of linked list

    public CustomLinkedHashMap() {
        table = new LinkedEntry[capacity];
    }
}

Step 3: Implement the put() Method

The put method will:

  1. Compute the bucket index using hashCode.
  2. Add the new entry at the head of the bucket chain (hashing).
  3. Add the entry at the end of the linked list (insertion order).
public void put(K key, V value) {
    int hash = Math.abs(key.hashCode()) % capacity;
    LinkedEntry newEntry = new LinkedEntry<>(key, value, null);

    if (table[hash] == null) {
        table[hash] = newEntry;
    } else {
        LinkedEntry current = table[hash];
        LinkedEntry prev = null;

        while (current != null) {
            if (current.key.equals(key)) {
                current.value = value; // update value
                return;
            }
            prev = current;
            current = current.next;
        }
        prev.next = newEntry;
    }

    // Maintain insertion order
    if (head == null) {
        head = tail = newEntry;
    } else {
        tail.after = newEntry;
        newEntry.before = tail;
        tail = newEntry;
    }
}

Step 4: Implement the get() Method

Retrieve value by key without disturbing order.

public V get(K key) {
    int hash = Math.abs(key.hashCode()) % capacity;
    LinkedEntry current = table[hash];

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

Step 5: Implement the remove() Method

Remove entry from both the bucket chain and the linked list.

public boolean remove(K key) {
    int hash = Math.abs(key.hashCode()) % capacity;
    LinkedEntry current = table[hash];
    LinkedEntry prev = null;

    while (current != null) {
        if (current.key.equals(key)) {
            if (prev == null) {
                table[hash] = current.next;
            } else {
                prev.next = current.next;
            }

            // Remove from linked list
            if (current.before != null) {
                current.before.after = current.after;
            } else {
                head = current.after;
            }
            if (current.after != null) {
                current.after.before = current.before;
            } else {
                tail = current.before;
            }
            return true;
        }
        prev = current;
        current = current.next;
    }
    return false;
}

Step 6: Implement display() Method

Display all entries in insertion order.

public void display() {
    LinkedEntry current = head;
    while (current != null) {
        System.out.print("[" + current.key + "=" + current.value + "] ");
        current = current.after;
    }
    System.out.println();
}

Step 7: Testing CustomLinkedHashMap

public class TestCustomLinkedHashMap {
    public static void main(String[] args) {
        CustomLinkedHashMap map = new CustomLinkedHashMap<>();

        map.put(1, "Java");
        map.put(2, "Python");
        map.put(3, "C++");
        map.put(17, "Go"); // collision with key=1

        map.display();

        System.out.println("Get key 2: " + map.get(2));
        map.remove(1);
        map.display();
    }
}

Expected Output

[1=Java] [2=Python] [3=C++] [17=Go] 
Get key 2: Python
[2=Python] [3=C++] [17=Go] 

This shows that insertion order is preserved even after deletions.

Custom LinkedHashMap Implementation

// Custom LinkedHashMap Implementation
// Preserves insertion order like java.util.LinkedHashMap

class LinkedEntry<K, V> {
    K key;
    V value;
    LinkedEntry<K, V> next;   // For bucket chaining (hash collisions)
    LinkedEntry<K, V> before; // Previous entry in insertion order
    LinkedEntry<K, V> after;  // Next entry in insertion order

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

public class CustomLinkedHashMap<K, V> {
    private int capacity = 16;
    private LinkedEntry<K, V>[] table;
    private LinkedEntry<K, V> head; // Start of insertion order
    private LinkedEntry<K, V> tail; // End of insertion order

    @SuppressWarnings("unchecked")
    public CustomLinkedHashMap() {
        table = new LinkedEntry[capacity];
    }

    // Step 1: Put method
    public void put(K key, V value) {
        int hash = Math.abs(key.hashCode()) % capacity;
        LinkedEntry<K, V> newEntry = new LinkedEntry<>(key, value, null);

        if (table[hash] == null) {
            table[hash] = newEntry;
        } else {
            LinkedEntry<K, V> current = table[hash];
            LinkedEntry<K, V> prev = null;

            while (current != null) {
                if (current.key.equals(key)) {
                    current.value = value; // update existing key
                    return;
                }
                prev = current;
                current = current.next;
            }
            prev.next = newEntry;
        }

        // Maintain insertion order
        if (head == null) {
            head = tail = newEntry;
        } else {
            tail.after = newEntry;
            newEntry.before = tail;
            tail = newEntry;
        }
    }

    // Step 2: Get method
    public V get(K key) {
        int hash = Math.abs(key.hashCode()) % capacity;
        LinkedEntry<K, V> current = table[hash];

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

    // Step 3: Remove method
    public boolean remove(K key) {
        int hash = Math.abs(key.hashCode()) % capacity;
        LinkedEntry<K, V> current = table[hash];
        LinkedEntry<K, V> prev = null;

        while (current != null) {
            if (current.key.equals(key)) {
                // Remove from hash bucket
                if (prev == null) {
                    table[hash] = current.next;
                } else {
                    prev.next = current.next;
                }

                // Remove from linked list (insertion order)
                if (current.before != null) {
                    current.before.after = current.after;
                } else {
                    head = current.after;
                }

                if (current.after != null) {
                    current.after.before = current.before;
                } else {
                    tail = current.before;
                }
                return true;
            }
            prev = current;
            current = current.next;
        }
        return false;
    }

    // Step 4: Display method — prints in insertion order
    public void display() {
        LinkedEntry<K, V> current = head;
        while (current != null) {
            System.out.print("[" + current.key + "=" + current.value + "] ");
            current = current.after;
        }
        System.out.println();
    }
}

// Step 5: Test the CustomLinkedHashMap
class TestCustomLinkedHashMap {
    public static void main(String[] args) {
        CustomLinkedHashMap<Integer, String> map = new CustomLinkedHashMap<>();

        map.put(1, "Java");
        map.put(2, "Python");
        map.put(3, "C++");
        map.put(17, "Go"); // Collision with key=1 (same bucket likely)

        map.display();

        System.out.println("Get key 2: " + map.get(2));

        map.remove(1);
        map.display();
    }
}

 Expected Output

[1=Java] [2=Python] [3=C++] [17=Go] 
Get key 2: Python
[2=Python] [3=C++] [17=Go] 

 

Advantages of LinkedHashMap

  • Maintains insertion or access order.
  • Provides predictable iteration order.
  • Almost the same performance as HashMap.
  • Useful in caching scenarios.

Disadvantages of LinkedHashMap

  • Slightly more memory usage due to linked list pointers.
  • Slightly slower than HashMap due to maintaining order.
  • Not synchronized (use Collections.synchronizedMap() or ConcurrentHashMap for thread safety).

Real-World Applications

  • Implementing LRU Caches.
  • Maintaining order-sensitive data, e.g., configuration settings.
  • Building index-based systems where order matters.

Conclusion

In this blog, we created a CustomLinkedHashMap to understand how Java’s LinkedHashMap preserves order while maintaining efficient access.
We learned:

  • How it combines a hash table with a doubly-linked list.
  • How insertion and deletion maintain order.
  • The internal mechanics behind one of Java’s most versatile data structures.

 

Next Blog- Custom Implementation of LinkedHashSet in Java

 

Sanjiv
0

You must logged in to post comments.

Related Blogs

Generics P...
Advanced Java August 08 ,2025

Generics Part- 2

Collection...
Advanced Java July 07 ,2025

Collections Framewor...

Mastering...
Advanced Java August 08 ,2025

Mastering Java Multi...

Annotation...
Advanced Java August 08 ,2025

Annotations

Java Memor...
Advanced Java August 08 ,2025

Java Memory Manageme...

Java Lambd...
Advanced Java August 08 ,2025

Java Lambda Expressi...

Java Funct...
Advanced Java August 08 ,2025

Java Functional Inte...

Java Strea...
Advanced Java August 08 ,2025

Java Stream API

JDBC (Java...
Advanced Java August 08 ,2025

JDBC (Java Database...

JDBC (Java...
Advanced Java September 09 ,2025

JDBC (Java Database...

Annotation...
Advanced Java August 08 ,2025

Annotations

Generics
Advanced Java August 08 ,2025

Generics

Java I/O (...
Advanced Java August 08 ,2025

Java I/O (NIO)

Introducti...
Advanced Java September 09 ,2025

Introduction to Desi...

Design Pat...
Advanced Java September 09 ,2025

Design Patterns in J...

Other Prin...
Advanced Java September 09 ,2025

Other Principles Beh...

Creational...
Advanced Java September 09 ,2025

Creational Design Pa...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

Creational...
Advanced Java September 09 ,2025

Creational Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Builder De...
Advanced Java September 09 ,2025

Builder Design Patte...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Design Pat...
Advanced Java September 09 ,2025

Design Patterns in J...

Chain of R...
Advanced Java September 09 ,2025

Chain of Responsibil...

Command De...
Advanced Java September 09 ,2025

Command Design Patte...

Interprete...
Advanced Java September 09 ,2025

Interpreter Design P...

Iterator D...
Advanced Java September 09 ,2025

Iterator Design Patt...

Mediator D...
Advanced Java September 09 ,2025

Mediator Design Patt...

Memento De...
Advanced Java September 09 ,2025

Memento Design Patte...

Observer D...
Advanced Java September 09 ,2025

Observer Design Patt...

State Desi...
Advanced Java September 09 ,2025

State Design Pattern...

Strategy D...
Advanced Java September 09 ,2025

Strategy Design Patt...

Template M...
Advanced Java September 09 ,2025

Template Method Desi...

Visitor De...
Advanced Java September 09 ,2025

Visitor Design Patte...

Prototype...
Advanced Java September 09 ,2025

Prototype Design Pat...

Java 8+ Fe...
Advanced Java October 10 ,2025

Java 8+ Features

SOLID Prin...
Advanced Java October 10 ,2025

SOLID Principles in...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

How Iterat...
Advanced Java October 10 ,2025

How Iterators Work i...

How Concur...
Advanced Java October 10 ,2025

How ConcurrentHashMa...

Comparable...
Advanced Java October 10 ,2025

Comparable vs Compar...

Get In Touch

G06, Kristal Olivine Bellandur near Bangalore Central Mall, Bangalore Karnataka, 560103

+91-8076082435

techiefreak87@gmail.com