Skip to content
intermediatePhase 13 · Java Collections

HashSet & TreeSet

Use HashSet for O(1) membership and TreeSet for sorted unique elements.

45m
3 problems
Topic Progress0%

HashSet

HashSet

HashSet is the most commonly used Set implementation. It is backed by a HashMap internally — each element is stored as a key in the HashMap with a dummy value.

Key characteristics:

  • Backed by HashMap
  • O(1) average for add, remove, contains
  • No ordering guarantees
  • Allows one null element
  • Not synchronized (not thread-safe)
import java.util.*;

public class HashSetDemo {
    public static void main(String[] args) {
        // Creating HashSet
        Set<String> colors = new HashSet<>();

        // Adding elements
        colors.add("Red");
        colors.add("Green");
        colors.add("Blue");
        colors.add("Red"); // duplicate - ignored
        colors.add(null); // one null allowed
        System.out.println("Set: " + colors); // order not guaranteed
        System.out.println("Size: " + colors.size()); // 4, not 5

        // Checking membership
        System.out.println("Contains Red: " + colors.contains("Red")); // true
        System.out.println("Contains Yellow: " + colors.contains("Yellow")); // false

        // Removing elements
        colors.remove("Blue");
        colors.remove("Yellow"); // no-op if not present
        System.out.println("After remove: " + colors);

        // Iterating
        for (String color : colors) {
            System.out.println("Color: " + color);
        }

        // Set operations
        Set<Integer> setA = new HashSet<>(Arrays.asList(1, 2, 3, 4));
        Set<Integer> setB = new HashSet<>(Arrays.asList(3, 4, 5, 6));

        // Union
        Set<Integer> union = new HashSet<>(setA);
        union.addAll(setB);
        System.out.println("\nUnion: " + union); // [1, 2, 3, 4, 5, 6]

        // Intersection
        Set<Integer> intersection = new HashSet<>(setA);
        intersection.retainAll(setB);
        System.out.println("Intersection: " + intersection); // [3, 4]

        // Difference (A - B)
        Set<Integer> difference = new HashSet<>(setA);
        difference.removeAll(setB);
        System.out.println("Difference (A-B): " + difference); // [1, 2]

        // Converting between collections
        List<String> list = Arrays.asList("a", "b", "a", "c");
        Set<String> unique = new HashSet<>(list); // remove duplicates
        System.out.println("\nUnique from list: " + unique);

        List<String> backToList = new ArrayList<>(unique);
        System.out.println("Back to list: " + backToList);
    }
}

Internal implementation: HashSet uses a HashMap where elements are keys and values are a shared dummy object:

// Simplified internal structure
// HashSet.add(e) is equivalent to:
// map.put(e, PRESENT)
// where PRESENT is a static final Object

This means HashSet inherits HashMap's performance characteristics: O(1) average for add/remove/contains, with occasional O(n) worst case for hash collisions.

TreeSet

TreeSet

TreeSet is a NavigableSet backed by a TreeMap (red-black tree). It keeps elements in sorted order and provides navigation methods.

Key characteristics:

  • Backed by TreeMap
  • O(log n) for add, remove, contains
  • Elements sorted by natural ordering or Comparator
  • No null elements (throws NullPointerException)
  • NavigableSet methods for finding nearest elements
import java.util.*;

public class TreeSetDemo {
    public static void main(String[] args) {
        // Natural ordering
        TreeSet<Integer> numbers = new TreeSet<>();
        numbers.add(30);
        numbers.add(10);
        numbers.add(50);
        numbers.add(20);
        numbers.add(40);
        System.out.println("Sorted: " + numbers); // [10, 20, 30, 40, 50]

        // Navigation methods
        System.out.println("First: " + numbers.first()); // 10
        System.out.println("Last: " + numbers.last()); // 50
        System.out.println("Lower(30): " + numbers.lower(30)); // 20
        System.out.println("Higher(30): " + numbers.higher(30)); // 40
        System.out.println("Floor(25): " + numbers.floor(25)); // 20
        System.out.println("Ceiling(25): " + numbers.ceiling(25)); // 30

        // Subset views
        System.out.println("HeadSet(30): " + numbers.headSet(30)); // [10, 20]
        System.out.println("TailSet(30): " + numbers.tailSet(30)); // [30, 40, 50]
        System.out.println("SubSet(20,40): " + numbers.subSet(20, 40)); // [20, 30]

        // Custom ordering with Comparator
        TreeSet<String> byLength = new TreeSet<>(Comparator.comparingInt(String::length)
                .thenComparing(Comparator.naturalOrder()));
        byLength.add("Banana");
        byLength.add("Apple");
        byLength.add("Cherry");
        byLength.add("Fig");
        byLength.add("Kiwi");
        System.out.println("\nBy length: " + byLength); // [Fig, Kiwi, Apple, Banana, Cherry]

        // Descending iteration
        System.out.println("Descending: " + numbers.descendingSet()); // [50, 40, 30, 20, 10]

        // Poll (remove while accessing)
        System.out.println("pollFirst: " + numbers.pollFirst()); // 10
        System.out.println("pollLast: " + numbers.pollLast()); // 50
        System.out.println("After polls: " + numbers); // [20, 30, 40]

        // Set operations with TreeSet
        TreeSet<Integer> setA = new TreeSet<>(Arrays.asList(1, 2, 3, 4, 5));
        TreeSet<Integer> setB = new TreeSet<>(Arrays.asList(3, 4, 5, 6, 7));
        System.out.println("\nUnion: " + union(setA, setB));
        System.out.println("Intersection: " + intersection(setA, setB));
    }

    public static <T> TreeSet<T> union(TreeSet<T> a, TreeSet<T> b) {
        TreeSet<T> result = new TreeSet<>(a);
        result.addAll(b);
        return result;
    }

    public static <T> TreeSet<T> intersection(TreeSet<T> a, TreeSet<T> b) {
        TreeSet<T> result = new TreeSet<>(a);
        result.retainAll(b);
        return result;
    }
}

When to use TreeSet:

  • You need sorted unique elements
  • You need navigation methods (lower, higher, floor, ceiling)
  • You need range views (headSet, tailSet, subSet)
  • You need the smallest or largest element efficiently

LinkedHashSet

LinkedHashSet

LinkedHashSet extends HashSet and maintains a doubly linked list across all elements. This preserves insertion order while maintaining O(1) average operations.

Key characteristics:

  • Maintains insertion order
  • O(1) average for add, remove, contains
  • Slightly more memory than HashSet (linked list overhead)
  • Slightly slower than HashSet due to maintaining linked list
import java.util.*;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        // Insertion order preserved
        LinkedHashSet<String> ordered = new LinkedHashSet<>();
        ordered.add("Banana");
        ordered.add("Apple");
        ordered.add("Cherry");
        ordered.add("Date");
        ordered.add("Banana"); // duplicate - ignored

        System.out.println("LinkedHashSet: " + ordered);
        // [Banana, Apple, Cherry, Date] - insertion order

        // Compare with HashSet
        Set<String> unordered = new HashSet<>();
        unordered.add("Banana");
        unordered.add("Apple");
        unordered.add("Cherry");
        unordered.add("Date");
        System.out.println("HashSet: " + unordered); // order not guaranteed

        // Useful for removing duplicates while preserving order
        List<String> listWithDuplicates = Arrays.asList(
            "Charlie", "Alice", "Bob", "Alice", "Charlie", "David"
        );
        LinkedHashSet<String> uniqueOrdered = new LinkedHashSet<>(listWithDuplicates);
        List<String> result = new ArrayList<>(uniqueOrdered);
        System.out.println("\nOriginal: " + listWithDuplicates);
        System.out.println("Unique ordered: " + result);
        // [Charlie, Alice, Bob, David] - no duplicates, insertion order

        // Iterating in insertion order
        System.out.println("\nIterating:");
        for (String s : uniqueOrdered) {
            System.out.println("  " + s);
        }

        // Set operations preserving order
        LinkedHashSet<Integer> setA = new LinkedHashSet<>(Arrays.asList(1, 2, 3));
        LinkedHashSet<Integer> setB = new LinkedHashSet<>(Arrays.asList(3, 4, 5));
        LinkedHashSet<Integer> union = new LinkedHashSet<>(setA);
        union.addAll(setB);
        System.out.println("\nUnion (ordered): " + union); // [1, 2, 3, 4, 5]
    }
}

When to use LinkedHashSet:

  • You need uniqueness with insertion order
  • You want to remove duplicates from a list while preserving order
  • You want predictable iteration order without sorting
  • You want better performance than TreeSet but with ordering

Set Operations

Set Operations and Time Complexity

Operation HashSet TreeSet LinkedHashSet
add(e) O(1) avg O(log n) O(1) avg
remove(e) O(1) avg O(log n) O(1) avg
contains(e) O(1) avg O(log n) O(1) avg
size() O(1) O(1) O(1)
iteration O(n) O(n) O(n)

Set operations:

  • addAll() — Union: O(n + m)
  • retainAll() — Intersection: O(n * m)
  • removeAll() — Difference: O(n * m)
import java.util.*;

public class SetOperationsDemo {
    public static void main(String[] args) {
        Set<Integer> setA = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
        Set<Integer> setB = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));

        // Union: A ∪ B
        Set<Integer> union = new HashSet<>(setA);
        union.addAll(setB);
        System.out.println("Union: " + union); // [1, 2, 3, 4, 5, 6, 7, 8]

        // Intersection: A ∩ B
        Set<Integer> intersection = new HashSet<>(setA);
        intersection.retainAll(setB);
        System.out.println("Intersection: " + intersection); // [4, 5]

        // Difference: A - B
        Set<Integer> diffAB = new HashSet<>(setA);
        diffAB.removeAll(setB);
        System.out.println("A - B: " + diffAB); // [1, 2, 3]

        // Difference: B - A
        Set<Integer> diffBA = new HashSet<>(setB);
        diffBA.removeAll(setA);
        System.out.println("B - A: " + diffBA); // [6, 7, 8]

        // Symmetric Difference: (A - B) ∪ (B - A)
        Set<Integer> symDiff = new HashSet<>(setA);
        symDiff.addAll(setB);
        Set<Integer> common = new HashSet<>(setA);
        common.retainAll(setB);
        symDiff.removeAll(common);
        System.out.println("Symmetric Diff: " + symDiff); // [1, 2, 3, 6, 7, 8]

        // Subset check
        Set<Integer> subset = new HashSet<>(Arrays.asList(1, 2, 3));
        System.out.println("Is subset? " + setA.containsAll(subset)); // true

        // Performance demonstration
        Set<Integer> large = new HashSet<>();
        for (int i = 0; i < 100000; i++) {
            large.add(i);
        }
        long start = System.nanoTime();
        large.contains(50000); // O(1)
        long time = System.nanoTime() - start;
        System.out.println("\nHashSet contains: " + time + " ns");

        // Converting between Set types
        Set<String> linked = new LinkedHashSet<>(Arrays.asList("c", "a", "b"));
        Set<String> tree = new TreeSet<>(linked); // sorted
        Set<String> hash = new HashSet<>(tree); // unordered
        System.out.println("Linked: " + linked); // [c, a, b]
        System.out.println("Tree: " + tree); // [a, b, c]
        System.out.println("Hash: " + hash); // [a, b, c] (order not guaranteed)
    }
}

Performance tips:

  • Use addAll/retainAll/removeAll for bulk operations
  • For intersection, iterate over the smaller set: smallSet.retainAll(largeSet)
  • HashSet is fastest for general-purpose use
  • Use LinkedHashSet when order matters
  • Use TreeSet when sorted order or navigation is needed

Practice Problems

0/3solved
Find Common Elements

Write a method that takes two integer arrays and returns a list of elements common to both arrays. No duplicates in the result.

Solution
import java.util.*;

public class CommonElements {
    public static List<Integer> findCommon(int[] a, int[] b) {
        Set<Integer> setA = new HashSet<>();
        for (int num : a) setA.add(num);
        Set<Integer> common = new LinkedHashSet<>();
        for (int num : b) {
            if (setA.contains(num)) common.add(num);
        }
        return new ArrayList<>(common);
    }
}
Check Subset

Write a method that checks if one set is a subset of another set. A set A is a subset of set B if all elements of A are contained in B.

Solution
import java.util.*;

public class SubsetChecker {
    public static <T> boolean isSubset(Set<T> a, Set<T> b) {
        return b.containsAll(a);
    }
}
Remove Duplicates Preserving Order

Write a method that takes a list and returns a new list with duplicates removed while preserving the original order of first occurrences.

Solution
import java.util.*;

public class OrderPreserver {
    public static <T> List<T> removeDuplicates(List<T> list) {
        return new ArrayList<>(new LinkedHashSet<>(list));
    }
}

Quiz

1. What data structure is HashSet backed by internally?

Question 1 options

2. Which Set implementation keeps elements sorted?

Question 2 options

3. What is the time complexity of HashSet.contains()?

Question 3 options

4. What is the primary purpose of Java HashSet, TreeSet, and LinkedHashSet?

Question 4 options

Flashcards

Question

What is the difference between HashSet, LinkedHashSet, and TreeSet?

Answer

HashSet: fastest, no ordering. LinkedHashSet: maintains insertion order. TreeSet: sorted order, O(log n). All reject duplicates.

Question

How do you remove duplicates from a List while preserving order?

Answer

new ArrayList<>(new LinkedHashSet<>(list)). LinkedHashSet removes duplicates while maintaining insertion order, then convert back to List.

Question

How do you perform union, intersection, and difference with Sets?

Answer

Union: addAll(). Intersection: retainAll(). Difference: removeAll(). Copy the first set before modifying to preserve the original.

Question

What is Java HashSet, TreeSet, and LinkedHashSet?

Answer

Java HashSet, TreeSet, and LinkedHashSet is a key concept in Java programming.

Question

When to use Java HashSet, TreeSet, and LinkedHashSet?

Answer

Use Java HashSet, TreeSet, and LinkedHashSet when building production systems that require reliability, scalability, and maintainability.

Revision Notes

Key Takeaways

  • 1.HashSet is backed by HashMap — each element is a key with dummy value
  • 2.TreeSet provides sorted order and navigation at O(log n) cost
  • 3.LinkedHashSet preserves insertion order with O(1) performance
  • 4.Use LinkedHashSet to remove duplicates while preserving order

Interview Tips

  • Explain that HashSet uses HashMap internally
  • Know the time complexities: O(1) for HashSet, O(log n) for TreeSet
  • Describe how to remove duplicates from a list preserving order
  • Discuss set operations and their performance implications

Cheat Sheet

HashSet, TreeSet, LinkedHashSet

HashSet

  • Backed by HashMap
  • O(1) average add/remove/contains
  • No ordering, one null allowed

TreeSet

  • Backed by TreeMap (red-black tree)
  • O(log n) add/remove/contains
  • Sorted order, no nulls
  • Navigation: lower, higher, floor, ceiling

LinkedHashSet

  • HashSet + doubly linked list
  • O(1) average, insertion order
  • Slightly more memory than HashSet

Set Operations

  • Union: setA.addAll(setB)
  • Intersection: setA.retainAll(setB)
  • Difference: setA.removeAll(setB)
  • All modify the first set — copy first!