HashSet

Introduction

A HashSet or any other Set is a collection that doesn’t allow you to add duplicate elements. If you want objects of a class to be stored in a Set, you must override equals() and hashCode() methods. You must go through the HashMap tutorial before going through this tutorial.

The important points about Java HashSet class are –

  • HashSet doesn’t allow adding duplicate elements.
  • HashSet allows null value.
  • HashSet class methods are not synchronized. So, they are not thread safe.
  • HashSet doesn’t maintain the insertion order.
HashSet

HashSet<E> is a class in Java Collection Framework that implements Set<E> interface. Here E indicates the type of element the set is going to hold. So, if you want to create a HasSet where the element type is String, you can write it like –

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

To add a value, you have to use the add() method.

set.add("India");
set.add("Canada");

To remove a value from the Set, you have to use remove() method.

set.remove("Canada");

To check, if the set contains a value, use contains() method.

boolean result = set.contains("India");

To get the size of the set, you can use the size() method.

set.size()
As Set doesn’t allow us to add duplicate elements, what will happen if we try to add duplicates?

If a Set already contains the element and if you try to add the same element, that add operation will be ignored.

How does HashSet work internally?

This is a shorter version of HashSet source code. We have removed many methods and fields for simplicity. We’ll only focus on a few methods.

public class HashSet<E> implements Set<E> {
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap<>();
    }

    public boolean add(E e) {
        return map.put(e, PRESENT) == null;
    }
	
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
}

As we can see, HashSet internally maintains a HashMap. When you add an element to HashSet, you just pass that element. That element is used as the key of the HashMap and a dummy object is used as value. As HashMap doesn’t allow you to put duplicate keys, if you try to add duplicate elements in a set, those duplicates will be ignored by the internal HashMap object.

If you want to check if the Set contains an element, HashSet just checks if the internal HashMap contains that key and based on the returns a boolean value.

When you try to remove an element, HashSet just removes that key from the internal Map.

So, HashSet just delegates the operations to the internal HashMap object. Pretty simple, right.

Other commonly used methods of HashSet
Method Description
void clear() Removes all of the elements from the set.
boolean isEmpty() Returns true if the set contains no elements.
int size() Returns the number of elements in the set.

Example:

public class HashSetDemo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<String>();
        set.add("India");
        set.add("Canada");
        set.add("USA");
        set.add("India");
        set.add("France");
        
        System.out.println(set);
        System.out.println(set.isEmpty());
        set.clear();
        System.out.println(set.size());
    }
}

Output:

[Canada, USA, France, India]
false
0
How to iterate over HashSet

There are multiple ways, but the most common one is to use a foreach loop.

HashSet<String> set = new HashSet<String>();
set.add("India");
set.add("Canada");
set.add("USA");
set.add("India");
set.add("France");

for (String element : set) {
    System.out.print(element + " ");
}

Output: Canada USA France India
As you said, HashSet doesn’t maintain the order in which the elements are added. Is there any way to preserve that order?

Yes. Instead of HashSet, you have to use LinkedHashSet.

public class HashSetDemo {
    public static void main(String[] args) {
        LinkedHashSet<String> set = new LinkedHashSet<String>();
        set.add("India");
        set.add("Canada");
        set.add("USA");
        set.add("India");
        set.add("France");
        
        for (String element : set) {
            System.out.print(element + " ");
        }
    }
}

Output: India Canada USA France
You have said, HashSet or any other Set doesn’t allow you to add duplicate elements. I believe this means a Set cannot contain duplicate elements.

Actually that is not true. Think about this – a Set doesn’t allow you to add duplicates, but once the element is added, Set doesn’t prevent you from updating that element. So, after adding an element, you can call the setter method to set a value in such a way that it will be a duplicate of another element. To understand it with an example, let’s create a CurrencyNote class.

public class CurrencyNote {
    private int value;
    
    public CurrencyNote(int value) {
        this.value = value;
    }

    public int getValue() { 
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    @Override
    public int hashCode() {
        return 31 + value;
    }

    @Override
    public boolean equals(Object obj) {
        return value == ((CurrencyNote) obj).value;
    }

    @Override
    public String toString() {
        return "value = " + value;
    }
}

As you can see, the equality is based on the value field. 

Now create a HashSet and add three elements where one element is duplicate.

LinkedHashSet<CurrencyNote> set = new LinkedHashSet<CurrencyNote>();
CurrencyNote note1 = new CurrencyNote(100);
CurrencyNote note2 = new CurrencyNote(200);
CurrencyNote note3 = new CurrencyNote(100);

set.add(note1);
set.add(note2);
set.add(note3);

System.out.println(set);

Output: [value = 100, value = 200]

You can see, note3 is a duplicate of note1. So only note1 and note2 are added and note3 is ignored by the set. So, currently the set contains two unique elements.

Now call the setValue() method against note2 and set the value to 100.

note2.setValue(100);
System.out.println(set);

Output: [value = 100, value = 100]

As you can see, currently our set contains 2 duplicate elements.

To avoid this situation, do not call the setter method once the objects are already added to the set or make the objects immutable.

Please remember, Set doesn’t allow you to add duplicates, but a set can contain duplicates.

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.