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
| Operation | Average Case | Worst 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
| Feature | HashSet | LinkedHashSet | 
|---|---|---|
| Order | No guarantee | Maintains insertion order | 
| Backing Structure | HashMap | LinkedHashMap | 
| Memory Overhead | Less | Slightly more | 
| Null Values | Allows one null | Allows 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
                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                
                                                                