HashSet in Java

Naveen AnamaneniUpdated · 4 min read

HashSet in Java is a class that implements the Set interface using a hash table. It is used to store unique elements and to check very quickly whether an element is present. HashSet does not keep elements in any particular order. It is the most commonly used Set. In this lesson, you will learn how to use HashSet, why its order looks random, and why your own classes need equals and hashCode to work with it.

Declaration

Declaration
public class HashSet<E> extends AbstractSet<E>
        implements Set<E>, Cloneable, Serializable

Features of HashSet

  • Unique elements: duplicates are ignored.
  • No order: the iteration order depends on hash codes and can change as the set grows.
  • Allows one null.
  • Fast: add, remove and contains take constant time on average.
  • Backed by a HashMap: each element is stored as a key (see "HashSet Internal Working").
  • Not synchronized.

Constructors

ConstructorDescription
HashSet()It creates an empty set with default capacity 16 and load factor 0.75.
HashSet(int initialCapacity)It creates an empty set with the given capacity.
HashSet(Collection<? extends E> c)It creates a set containing the elements of another collection.

Example: Basic HashSet Operations

Let's see an example of adding, checking and removing.

HashSetDemo.java▶ Try it
import java.util.HashSet;
import java.util.Set;
 
public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> emails = new HashSet<>();
        emails.add("navika@mail.com");
        emails.add("srihitha@mail.com");
        emails.add("navika@mail.com");
        emails.add("advitha@mail.com");
 
        System.out.println("Count: " + emails.size());
        System.out.println("Has Srihitha? "
            + emails.contains("srihitha@mail.com"));
 
        emails.remove("advitha@mail.com");
        System.out.println("Count after remove: " + emails.size());
    }
}
Output
Count: 3
Has Srihitha? true
Count after remove: 2

Explanation: Navika's email is added twice but stored once, so the count is 3. contains finds an element without looking through the whole set, and remove deletes one.

Example: Removing Duplicates from a List

Dedupe.java▶ Try it
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
 
public class Dedupe {
    public static void main(String[] args) {
        List<Integer> marks = List.of(85, 91, 85, 78, 91, 85);
        Set<Integer> unique = new HashSet<>(marks);
        System.out.println("Unique count: " + unique.size());
        List<Integer> sorted = new ArrayList<>(unique);
        sorted.sort(null);
        System.out.println("Unique sorted: " + sorted);
    }
}
Output
Unique count: 3
Unique sorted: [78, 85, 91]

Explanation: Passing the list to the HashSet constructor removes every duplicate in one step. Because a HashSet has no order, the result is copied into a list and sorted before printing (sort(null) means natural order).

equals and hashCode Are Required

HashSet decides whether two objects are the "same" using hashCode and equals. A class that doesn't override them uses object identity, so two objects with the same data are treated as different:

NeedsEquals.java▶ Try it
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
 
public class NeedsEquals {
    static class Plain {
        String name;
        Plain(String name) { this.name = name; }
    }
 
    static class Proper {
        String name;
        Proper(String name) { this.name = name; }
 
        @Override
        public boolean equals(Object o) {
            return o instanceof Proper p && name.equals(p.name);
        }
 
        @Override
        public int hashCode() {
            return Objects.hash(name);
        }
    }
 
    public static void main(String[] args) {
        Set<Plain> plain = new HashSet<>();
        plain.add(new Plain("Navika"));
        plain.add(new Plain("Navika"));
        System.out.println("Without equals/hashCode: " + plain.size());
 
        Set<Proper> proper = new HashSet<>();
        proper.add(new Proper("Navika"));
        proper.add(new Proper("Navika"));
        System.out.println("With equals/hashCode:    " + proper.size());
    }
}
Output
Without equals/hashCode: 2
With equals/hashCode:    1

Explanation: The two Plain objects have different identity hash codes, so both are kept. Proper defines equality by name, so the second Navika is recognized as a duplicate. A record gets correct equals and hashCode automatically.

Common Mistakes

Relying on the printed order of a HashSet. It isn't guaranteed and can change.

Overriding equals without hashCode. Equal objects then land in different buckets and both are kept.

Calling get(0) on a set. Sets have no index; iterate instead.

Interview Corner

How does HashSet prevent duplicates?

HashSet stores each element as a key in an internal HashMap. When adding, it computes the element's hashCode to find a bucket and uses equals to compare with the elements already there. If an equal element exists, add returns false and nothing changes. This is why elements need consistent equals and hashCode implementations.

Practice

  1. Easy: Read ten numbers and print how many distinct numbers there were.
  2. Harder: Create a Book class with ISBN and title, where two books are equal when their ISBNs match. Add three books, two with the same ISBN, to a HashSet and print the size.

Quick check: HashSet

4 questions
1Does HashSet keep insertion order?
2How many null elements can a HashSet hold?
3What does HashSet use to detect duplicates?
4What is the average cost of contains in a HashSet?

Key Takeaways

Was this tutorial helpful?

Lesson 273 of 482