Advanced Java October 06 ,2025

Custom Implementation of LinkedHashSet in Java

Combining Uniqueness with Order Preservation

Introduction

A LinkedHashSet in Java combines the unique element guarantee of a HashSet with the predictable iteration order of a LinkedHashMap.
Essentially, it is a hybrid that maintains insertion order while ensuring that no duplicates exist.

LinkedHashSet is widely used when:

  • You need to remove duplicates but maintain order.
  • You require fast lookups along with predictable iteration order.

In this blog, we will:

  • Understand the internal working of LinkedHashSet.
  • Learn why it internally uses LinkedHashMap.
  • Build a custom implementation step-by-step.
  • Compare it with HashSet and LinkedHashMap.
  • Explore advantages, disadvantages, and applications.

How LinkedHashSet Works Internally

In Java, LinkedHashSet is implemented internally using a LinkedHashMap.
When an element is added to a LinkedHashSet, it is stored as a key in the backing LinkedHashMap, with a constant dummy object as its value.

Example:

LinkedHashSet set = new LinkedHashSet<>();
set.add("Java");
set.add("Python");
set.add("C++");

Internally:

LinkedHashMap:
[Java=null, Python=null, C++=null]

Thus:

  • Uniqueness comes from HashMap keys.
  • Order preservation comes from LinkedHashMap’s linked list mechanism.

Designing a Custom LinkedHashSet

Since LinkedHashSet relies on LinkedHashMap, our custom version will use our previously implemented CustomLinkedHashMap.

Step 1: Define the CustomLinkedHashSet Class

public class CustomLinkedHashSet {
    private static final Object DUMMY = new Object();
    private CustomLinkedHashMap map;

    public CustomLinkedHashSet() {
        map = new CustomLinkedHashMap<>();
    }
}

Here:

  • We store all set elements as keys in our CustomLinkedHashMap.
  • A single dummy object is used as the value.

Step 2: Implement the add() Method

Adds an element to the set if it doesn’t already exist.

public boolean add(K key) {
    if (map.get(key) == null) {
        map.put(key, DUMMY);
        return true;
    }
    return false; // element already exists
}

Step 3: Implement the contains() Method

Checks if an element exists in the set.

public boolean contains(K key) {
    return map.get(key) != null;
}

Step 4: Implement the remove() Method

Removes the element from the set.

public boolean remove(K key) {
    return map.remove(key);
}

Step 5: Implement the display() Method

Displays elements in insertion order.

public void display() {
    map.display();
}

Step 6: Testing CustomLinkedHashSet

public class TestCustomLinkedHashSet {
    public static void main(String[] args) {
        CustomLinkedHashSet set = new CustomLinkedHashSet<>();

        set.add("Java");
        set.add("Python");
        set.add("C++");
        set.add("Java"); // duplicate element

        set.display();

        System.out.println("Contains Python? " + set.contains("Python"));
        System.out.println("Contains Ruby? " + set.contains("Ruby"));

        set.remove("Python");
        set.display();
    }
}

Expected Output

[Java] [Python] [C++] 
Contains Python? true
Contains Ruby? false
[Java] [C++] 

This output shows:

  • Uniqueness is preserved (Java wasn’t added twice).
  • Insertion order is maintained even after removal.

 Custom LinkedHashSet Implementation

// Custom LinkedHashMap Implementation (used internally by LinkedHashSet)
class LinkedEntry<K, V> {
    K key;
    V value;
    LinkedEntry<K, V> next;   // for bucket chaining
    LinkedEntry<K, V> before; // previous in insertion order
    LinkedEntry<K, V> after;  // next in insertion order

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

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];
    }

    // Add or update key-value pair
    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;
        }
    }

    // Retrieve value by key
    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;
    }

    // Remove a key-value pair
    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 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;
    }

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

// Custom LinkedHashSet Implementation
public class CustomLinkedHashSet<K> {
    private static final Object DUMMY = new Object();
    private CustomLinkedHashMap<K, Object> map;

    public CustomLinkedHashSet() {
        map = new CustomLinkedHashMap<>();
    }

    // Add element if not already present
    public boolean add(K key) {
        if (map.get(key) == null) {
            map.put(key, DUMMY);
            return true;
        }
        return false;
    }

    // Check if element exists
    public boolean contains(K key) {
        return map.get(key) != null;
    }

    // Remove element
    public boolean remove(K key) {
        return map.remove(key);
    }

    // Display elements in insertion order
    public void display() {
        map.display();
    }
}

// Testing CustomLinkedHashSet
class TestCustomLinkedHashSet {
    public static void main(String[] args) {
        CustomLinkedHashSet<String> set = new CustomLinkedHashSet<>();

        set.add("Java");
        set.add("Python");
        set.add("C++");
        set.add("Java"); // duplicate

        set.display();

        System.out.println("Contains Python? " + set.contains("Python"));
        System.out.println("Contains Ruby? " + set.contains("Ruby"));

        set.remove("Python");
        set.display();
    }
}

 Expected Output

[Java] [Python] [C++] 
Contains Python? true
Contains Ruby? false
[Java] [C++] 

This implementation ensures:

  • Uniqueness (no duplicates allowed)
  • Preserved insertion order
  • Built entirely on top of your CustomLinkedHashMap

 

Time Complexity of LinkedHashSet Operations

OperationAverage CaseWorst Case
add()O(1)O(n)
contains()O(1)O(n)
remove()O(1)O(n)

Performance depends on the underlying LinkedHashMap, which uses hashing and chaining/tree structures for collision handling.

Differences Between HashSet and LinkedHashSet

FeatureHashSetLinkedHashSet
OrderNo guaranteeMaintains insertion order
Backing StructureHashMapLinkedHashMap
Memory OverheadLessSlightly more
Null ValuesAllows one nullAllows one null

Advantages of LinkedHashSet

  • Maintains insertion order.
  • Ensures uniqueness.
  • Allows fast lookups (O(1) average case).
  • Useful for applications requiring order preservation and uniqueness.

Disadvantages of LinkedHashSet

  • Slightly higher memory consumption than HashSet.
  • Slightly slower than HashSet due to linked list maintenance.
  • Not synchronized (use Collections.synchronizedSet() for thread safety).

Real-World Applications

  • Removing duplicates from a collection while preserving order.
  • Maintaining an ordered set of unique configuration entries.
  • Building LRU caches.
  • Storing unique items in predictable order for reporting or display.

Conclusion

By implementing a CustomLinkedHashSet, we have:

  • Seen how Java’s LinkedHashSet is a wrapper around LinkedHashMap.
  • Understood the importance of order preservation and uniqueness.
  • Built a working model that demonstrates these principles in action.

LinkedHashSet is especially useful in cases where order matters but uniqueness cannot be compromised.

 

Next Blog- Custom Implementation of ArrayList 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