DEV Community

Mohammed mhanna
Mohammed mhanna

Posted on

๐Ÿง  Mastering Data Structures in Java โ€” Part 6: HashSet

๐Ÿ” What Is a HashSet?

A HashSet is a collection in Java that stores unique elements โ€” no duplicates allowed.
Itโ€™s part of the java.util package and is backed by a HashMap internally.

Think of it like a bag of unique cards ๐ŸŽด โ€” if you try to add the same card again, it just ignores it.


โš™๏ธ Quick Example

import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        HashSet<String> countries = new HashSet<>();

        countries.add("Japan");
        countries.add("Canada");
        countries.add("Japan"); // duplicate ignored

        System.out.println(countries); // [Japan, Canada]
        System.out.println("Contains Canada? " + countries.contains("Canada")); // true
    }
}
Enter fullscreen mode Exit fullscreen mode

โœ… Output:

[Japan, Canada]
Contains Canada? true
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Notice how โ€œJapanโ€ appears only once โ€” HashSet automatically handles duplicates.


โš™๏ธ How It Works Internally

Under the hood, a HashSet uses a HashMap where:

Elements are stored as keys.

A dummy value (usually a static constant) acts as the value.

When you call add(element):

The elementโ€™s hashCode() is computed.

Itโ€™s placed into a bucket based on that hash.

If another element has the same hash (collision), itโ€™s stored in a linked list or tree within that bucket.

The equals() method ensures duplicates are not added.


โš™๏ธ Common Methods

Method Description
add(E e) Adds element if not already present
remove(Object o) Removes element
contains(Object o) Checks if element exists
isEmpty() Returns true if empty
size() Returns number of elements
clear() Removes all elements

๐Ÿงฎ Time Complexity

Operation Average Worst Case
add() O(1) O(n)
remove() O(1) O(n)
contains() O(1) O(n)
iteration O(n) O(n)

โšก HashSet provides constant-time performance for most operations โ€” thatโ€™s why itโ€™s widely used for lookups and membership checks.


๐Ÿ’ก Key Features

๐Ÿšซ No duplicates โ€” each element is unique.

โšก Fast lookup using hashing.

๐Ÿ”€ No guaranteed order โ€” iteration order may differ from insertion order.

๐Ÿ“ฆ Allows one null element.

โŒ Not thread-safe โ€” use Collections.synchronizedSet() for concurrency.


๐Ÿ” Example: Removing Duplicates from a List

import java.util.*;

public class RemoveDuplicates {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
        HashSet<Integer> unique = new HashSet<>(numbers);

        System.out.println(unique); // [1, 2, 3, 4, 5]
    }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ This is one of the most common and elegant uses of a HashSet.


๐Ÿ” Example: Checking Membership Efficiently

HashSet<String> users = new HashSet<>();
users.add("mohammed");
users.add("ahmed");

if (users.contains("mohammed")) {
    System.out.println("Welcome back!");
}
Enter fullscreen mode Exit fullscreen mode

This operation is instant (O(1)) โ€” much faster than searching a list.


๐Ÿงฉ HashSet vs TreeSet vs LinkedHashSet

Feature HashSet LinkedHashSet TreeSet
Order No order Maintains insertion order Sorted order
Speed Fastest Slightly slower Slower (due to sorting)
Backed By HashMap LinkedHashMap TreeMap
Allows Null Yes Yes No
Use Case Unordered unique items Preserve insertion order Sorted unique items

๐ŸŒ Real-World Uses of HashSet

  1. ๐Ÿงพ Removing Duplicates

Cleaning up data โ€” for example, user emails or IDs that appear multiple times.

  1. ๐Ÿ” Checking Membership

Efficient โ€œalready exists?โ€ checks (e.g., registered usernames, cached results).

  1. ๐Ÿง  Tracking Unique Events

Game achievements, visited URLs, or user actions that should happen only once.

  1. ๐Ÿ—ƒ Efficient Lookup Tables

Fast lookups for values without caring about order โ€” like tags, keywords, or filters.


โš™๏ธ Example: Unique Visitors in a Web App

import java.util.HashSet;

public class UniqueVisitors {
    public static void main(String[] args) {
        HashSet<String> visitors = new HashSet<>();

        visitors.add("user123");
        visitors.add("guest456");
        visitors.add("user123"); // duplicate

        System.out.println("Unique visitors: " + visitors.size());
    }
}
Enter fullscreen mode Exit fullscreen mode

โœ… Output:

Unique visitors: 2


โš ๏ธ Common Mistakes

โŒ Expecting insertion order โ€” HashSet does not maintain it.

โŒ Forgetting to override hashCode() and equals() in custom objects.

โŒ Using mutable objects as keys โ€” changing them breaks hash consistency.


๐Ÿงฉ Example: Custom Object in HashSet

import java.util.*;

class Student {
    String name;
    int id;

    Student(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student s = (Student) o;
        return id == s.id;
    }
}

public class CustomHashSet {
    public static void main(String[] args) {
        HashSet<Student> set = new HashSet<>();

        set.add(new Student("Ali", 1));
        set.add(new Student("Ali", 1)); // duplicate ignored

        System.out.println("Total students: " + set.size()); // 1
    }
}
Enter fullscreen mode Exit fullscreen mode

Without overriding hashCode() and equals(), this would incorrectly count as 2 students.


๐Ÿ’ก Final Thought

The HashSet is your go-to structure when you want uniqueness and speed without worrying about order.
Itโ€™s one of the fastest and most memory-efficient collections in Java โ€” a real workhorse behind the scenes of many high-performance systems. โšก

Top comments (0)