Java HashSet is a hash table data structure implementation of the Set interface based on a HashMap, a part of the Java Collections Framework

HashSet has the following attributes

  • No guarantees to the iteration order

  • Provides contains(Object), add(Object) and remove(Object) operations in constant-time performance

  • Duplicated elements are ignored. Null element is permitted

  • Compares elements based on theirs equals and hashCode

  • Unsynchronized implementation. In multi-threading environment with at least one thread modifies the list, it must be synchronized externally

Lets walk through this tutorial to explore them in more details

The class hierarchy

HashSet implements the Set interface which extends from Collection and Iterable interfaces hierarchically

HashSet vs LinkedHashSet and TreeSet

HashSet has no ordering guarantees and run faster than TreeSet (constant-time vs log-time for most operations)

LinkedHashSet provides insertion-ordered iteration and run nearly as fast as HashSet

TreeSet provides value-ordered iteration. TreeSet can be used to sort a HashSet or LinkedHashSet

Declare a HashSet

As a result from the class hierarchy, you can declare a HashSet with the following ways

@Test
public void declare() {  
    Set<Integer> set1 = new HashSet<>();
    assertThat(set1).isInstanceOf(HashSet.class);

    HashSet<Integer> set2 = new HashSet<>();
}

Create and Initialize

Provide either Set.of or List.of factory method, since Java 9, or Arrays.asList factory method to the HashSet(Collection) constructor to create and init a HashSet in one line at the creation time

You can also use add or addAll method to initialize a HashSet after the creation time

@Test
public void initializeWithAdd() {  
    // Create a new HashSet
    Set<Integer> set = new HashSet<>();

    // Add elements to HashSet
    set.add(3);
    set.add(1);
    set.add(2);

    // Can add null element
    set.add(null);

    // Duplicate element will be ignored
    set.add(2);

    assertThat(set).hasSize(4);

    // The output ordering will be vary as Set is not reserved the insertion order
    System.out.println(set);
}

Learn more

Iterate

Iterate over a HashSet by using Java 8+ forEach(Consumer)

@Test
public void iterate() {  
    Set<Integer> set = new HashSet<>(Set.of(3, 1, 2));

    set.forEach(ele -> System.out.printf("%d ", ele));
}

Learn more

Filter and Retrieve

You can filter a HashSet by using Java 8+ Stream APIs

@Test
public void filter() {  
    Set<Integer> set = new HashSet<>(Set.of(3, 1, 2));
    Integer[] arr = set.stream().filter(e -> e >= 2).toArray(Integer[]::new);

    assertThat(arr).contains(2, 3);
}

Learn more

Examine, Add and Remove

  • HashSet provides contains(Object), add(Object) and remove(Object) methods in a constant-time performance to examine if a HashSet contains the specified element, to add a new element, and remove an existing element from a HashSet respectively
@Test
public void containsAddRemoveSingleElement() {  
    Set<Integer> hashSet = new HashSet<>(Set.of(1, 2, 3));
    Boolean contained = hashSet.contains(3);
    assertThat(contained).isTrue();

    hashSet.add(4);
    hashSet.add(4);
    hashSet.add(null);
    assertThat(hashSet).contains(1, 2, 3, 4, null);

    hashSet.remove(2);
    assertThat(hashSet).contains(1, 3, 4, null);
}
  • You can also do the above operations in bulk with containsAll, addAll, removeAll, Java 8+ removeIf
@Test
public void containsAddRemoveMultipleElements() {  
    Set<Integer> hashSet = new HashSet<>(Set.of(1, 2, 3));
    Boolean contained = hashSet.containsAll(Set.of(1, 2));
    assertThat(contained).isTrue();

    hashSet.addAll(Set.of(3, 4, 5));
    assertThat(hashSet).contains(1, 2, 3, 4, 5);

    hashSet.removeAll(Set.of(1, 2, 6));
    assertThat(hashSet).contains(3, 4, 5);

    hashSet.removeIf(e -> e >= 4);
    assertThat(hashSet).contains(3);
}

Objects comparing in HashSet

  • Internally, HashSet basic operations such as contains, add and remove work based on comparing its element objects which depend on their equals and hashCode implementation

    In the following example, hashSet.contains(new Book(1, "a")) should return true but false, hashSet.add(new Book(1, "a")) should not but success and hashSet.remove(new Book(1, "a")) should success but not , due to lack of equals and hashCode implementation on the custom class

@Test
public void objectsComparingProblem(){  
    Set<Book> hashSet = new HashSet<>();
    hashSet.add(new Book(1, "a"));

    Boolean contained = hashSet.contains(new Book(1, "a"));
    assertThat(contained).isFalse();

    hashSet.add(new Book(1, "a"));
    assertThat(hashSet).hasSize(2);

    hashSet.remove(new Book(1, "a"));
    assertThat(hashSet).hasSize(2);
}

static class Book {  
    int id;
    String title;

    Book(int id, String title) {
        this.id = id;
        this.title = title;
    }

    int getId() {
        return id;
    }

    String getTitle() {
        return title;
    }
}
  • You can fix the above problem by overriding equals and hashCode as the below example
@Test
public void objectsComparingFixed(){  
    Set<BookFixed> hashSet = new HashSet<>();
    hashSet.add(new BookFixed(1, "a"));

    Boolean contained = hashSet.contains(new BookFixed(1, "a"));
    assertThat(contained).isTrue();

    hashSet.add(new BookFixed(1, "a"));
    assertThat(hashSet).hasSize(1);

    hashSet.remove(new BookFixed(1, "a"));
    assertThat(hashSet).hasSize(0);
}

static class BookFixed {  
    int id;
    String title;

    BookFixed(int id, String title) {
        this.id = id;
        this.title = title;
    }

    int getId() {
        return id;
    }

    String getTitle() {
        return title;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BookFixed bookFixed = (BookFixed) o;
        return id == bookFixed.id &&
                Objects.equals(title, bookFixed.title);
    }

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

Sort a HashSet

Java doesn't have a direct API to sort a HashSet, you have to convert it to TreeSet or ArrayList

@Test
public void sort() {  
    Set<Integer> set = new HashSet<>(Set.of(3, 1, 2));
    NavigableSet<Integer> sortedSet = new TreeSet<>(set);

    // Asserts that the set contains the given values in order
    assertThat(sortedSet).containsExactly(1, 2, 3);
}

Learn more

Conclusion

In this tutorial, we learned about HashSet class hierarchy, features and operations. You can find below the full source code