← Back to Home

HashSet

HashSet is one of the most commonly used implementations of the Set interface in the Java Collections Framework. It stores unique elements and provides fast performance for basic operations using hashing. This is a very high-frequency interview topic.

What Is HashSet?

  • Implementation of the Set interface
  • Stores unique elements only
  • Does not maintain insertion order
  • Allows at most one null
  • Part of java.util package
Set<String> set = new HashSet<>();
          

Position in Collection Hierarchy

Iterable
   └── Collection
        └── Set
             └── HashSet
          

How HashSet Works Internally (Interview Favorite)

  • Backed by a HashMap internally
  • Elements stored as keys
  • Dummy object used as value
private static final Object PRESENT = new Object();
          
  • ✔ Fast lookup
  • ✔ No duplicate keys allowed

Key Characteristics of HashSet

  • Unordered (no guarantee of order)
  • Average time complexity O(1)
  • Uses hashCode() and equals()
  • Not synchronized (not thread-safe)

Creating a HashSet

HashSet<Integer> set = new HashSet<>();
          

With Initial Capacity & Load Factor

HashSet<Integer> set = new HashSet<>(16, 0.75f);
          
  • Default capacity = 16
  • Default load factor = 0.75

Commonly Used Methods

set.add("Java");
set.remove("Java");
set.contains("Java");
set.size();
set.isEmpty();
          

Duplicate Handling Example

HashSet<String> set = new HashSet<>();
set.add("Java");
set.add("Java");

System.out.println(set); // [Java]
          
  • ✔ Duplicate ignored
  • ✔ No exception thrown

Importance of equals() and hashCode() (Critical)

HashSet uniqueness depends on:

  • hashCode() → bucket location
  • equals() → equality check
class Employee {
    int id;
}
          

⚠️ Without overriding both methods, duplicates may be stored logically.

Null Handling

  • Allows one null element
set.add(null);
set.add(null); // ignored
          

Performance Complexity

Operation Time Complexity
add O(1) average
remove O(1)
contains O(1)
iteration O(n)

HashSet vs LinkedHashSet vs TreeSet

Feature HashSet LinkedHashSet TreeSet
Order No Insertion Sorted
Performance Fastest Slightly slower Slowest
Null allowed One One ❌ No
Internal DS HashMap LinkedHashMap Red-Black Tree

Thread Safety

  • Not thread-safe

Thread-safe Alternatives: Collections.synchronizedSet(), CopyOnWriteArraySet

When to Use HashSet

  • Need unique elements
  • Fast lookup required
  • Order does not matter
  • Membership testing

Common Beginner Mistakes

  • Expecting insertion order
  • Forgetting to override equals() and hashCode()
  • Using mutable objects as elements
  • Using HashSet in concurrent scenarios without synchronization

Interview-Ready Answers

Short Answer

HashSet is a Set implementation that stores unique elements using hashing.

Detailed Answer

In Java, HashSet implements the Set interface and uses a HashMap internally to store elements as keys. It ensures uniqueness using hashCode() and equals(), provides constant-time performance for basic operations, and does not maintain insertion order.

Key Takeaway

Use HashSet when uniqueness and performance matter more than order. Proper implementation of equals() and hashCode() is essential for correctness.

HashSet Examples

1. Creating a HashSet

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("Java");
        set.add("API");
        System.out.println(set);
    }
}
          

Output

[Java, API]

2. HashSet Does Not Allow Duplicates

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<Integer> set = new HashSet<>();
        set.add(10);
        set.add(10);
        set.add(20);
        System.out.println(set);
    }
}
          

Output

[10, 20]

3. No Guaranteed Order in HashSet

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("B");
        set.add("A");
        set.add("C");
        System.out.println(set);
    }
}
          

Explanation

Order may vary on each run.

4. HashSet Allows One null

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add(null);
        set.add(null);
        set.add("Java");
        System.out.println(set);
    }
}
          

Output

[null, Java]

5. Checking Element Existence (contains)

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>(Arrays.asList("Java", "API"));
        System.out.println(set.contains("Java"));
    }
}
          

Output

true

6. Removing Elements

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
        set.remove(2);
        System.out.println(set);
    }
}
          

Output

[1, 3]

7. Iterating Using for-each

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>(Arrays.asList("A", "B", "C"));
        for (String s : set) {
            System.out.println(s);
        }
    }
}
          

8. Iterating Using Iterator

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<Integer> set = new HashSet<>(Arrays.asList(10, 20, 30));
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}
          

9. Removing While Iterating (Correct Way)

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()) {
            if (it.next() == 2) {
                it.remove();
            }
        }
        System.out.println(set);
    }
}
          

Output

[1, 3]

10. Converting List to HashSet (Remove Duplicates)

import java.util.*;

class Demo {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 2, 3);
        HashSet<Integer> set = new HashSet<>(list);
        System.out.println(set);
    }
}
          

Output

[1, 2, 3]

11. Converting HashSet to List

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>(Arrays.asList("A", "B"));
        List<String> list = new ArrayList<>(set);
        System.out.println(list);
    }
}
          

12. isEmpty() and size()

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        System.out.println(set.isEmpty());
        set.add("Java");
        System.out.println(set.size());
    }
}
          

Output

true
1
          

13. HashSet with Custom Objects (Without equals/hashCode)

class User {
    int id;
    User(int id) { this.id = id; }
}

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<User> set = new HashSet<>();
        set.add(new User(1));
        set.add(new User(1));
        System.out.println(set.size());
    }
}
          

Output

2

14. HashSet with Custom Objects (With equals & hashCode)

class User {
    int id;
    User(int id) { this.id = id; }

    public boolean equals(Object o) {
        return this.id == ((User)o).id;
    }

    public int hashCode() {
        return id;
    }
}

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<User> set = new HashSet<>();
        set.add(new User(1));
        set.add(new User(1));
        System.out.println(set.size());
    }
}
          

Output

1

15. Internal Working (Conceptual Example)

HashSet<String> set = new HashSet<>();
set.add("Java");
          

Explanation

  • Uses HashMap internally
  • Elements stored as keys
  • Dummy value is used internally

16. HashSet vs LinkedHashSet (Order)

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> h = new HashSet<>();
        h.add("B");
        h.add("A");

        LinkedHashSet<String> l = new LinkedHashSet<>();
        l.add("B");
        l.add("A");

        System.out.println(h);
        System.out.println(l);
    }
}
          

Output

[A, B]
[B, A]
          

17. Thread-Safe HashSet

import java.util.*;

class Demo {
    public static void main(String[] args) {
        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        set.add("Safe");
        System.out.println(set);
    }
}
          

18. retainAll() – Intersection

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<Integer> s1 = new HashSet<>(Arrays.asList(1, 2, 3));
        HashSet<Integer> s2 = new HashSet<>(Arrays.asList(2, 3, 4));
        s1.retainAll(s2);
        System.out.println(s1);
    }
}
          

Output

[2, 3]

19. addAll() – Union

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<Integer> s1 = new HashSet<>(Arrays.asList(1, 2));
        HashSet<Integer> s2 = new HashSet<>(Arrays.asList(2, 3));
        s1.addAll(s2);
        System.out.println(s1);
    }
}
          

Output

[1, 2, 3]

20. Interview Summary – HashSet

import java.util.*;

class Demo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("Unique");
        set.add("Unique");
        System.out.println(set);
    }
}
          

Key Points

  • No duplicates
  • Allows one null
  • No insertion order
  • Backed by HashMap
  • Depends on hashCode() & equals()