Java HashSet is the most popular implementation of Set interface. java.util.HashSet is backed by a HashMap. HashSet extends AbstractSet class and implements Set, Cloneable and Serializable interfaces.
Java HashSet
Some of the important points about HashSet in java are;
- HashSet doesn’t allow duplicate entries.
- HashSet allows null as a value.
- HashSet doesn’t guarantee the insertion order of elements.
- HashSet is not thread-safe. You can get thread-safe HashSet using
Collections.synchronizedSet
method at the cost of performance. You can also useCopyOnWriteArraySet
concurrency class for thread safety. - HashSet iterator methods are fail-fast. So any structural modification to the set after creation of iterator will throw ConcurrentModificationException.
- HashSet supports Generics, this is the recommended approach to avoid ClassCastException at runtime.
- HashSet uses HashMap for storing elements, so the objects should provide good implementation of hashCode() and equals() method to avoid unwanted results.
Java HashSet Constructors
Java HashSet provides four constructors.
- public HashSet(): Creates a new empty HashSet, the backing HashMap is initialized with default initial capacity as 16 and load factor 0.75.
- public HashSet(int initialCapacity): Creates a empty HashSet with backing HashMap initialized with specified capacity and load factor 0.75.
- public HashSet(int initialCapacity, float loadFactor): Creates a empty HashSet with backing HashMap initialized with specified capacity and specified load factor.
- public HashSet(Collection<? extends E> c): Creates a new Set containing the elements in the specified collection. The backing HashMap is created with default load factor (0.75) and an initial capacity sufficient enough to contain all the elements in the specified collection.
Below code snippet is showing all these HashSet constructors example usage.
1 2 3 4 5 6 7 8 9 10 |
Set<String> set = new HashSet<>(); //initial capacity should be power of 2 set = new HashSet<>(32); //setting backing HashMap initial capacity and load factor set = new HashSet<>(32, 0.80f); //creating HashSet from another Collection Set<String> set1 = new HashSet<>(set); Set<String> set2 = new HashSet<>(new ArrayList<>()); |
Java HashSet Methods
Some of the useful HashSet methods are;
- public boolean add(E e): Adds the given element to the Set if not already present. This method internally uses equals() method to check for duplicates, so make sure your object defines equals() method properly.
- public void clear(): Removes all the elements from the Set.
- public Object clone(): Returns a shallow copy of the Set instance.
- public boolean contains(Object o): Returns true if the Set contains the given element, othrweise false.
- public boolean isEmpty(): Returns true if Set contains no elements, otherwise false.
- public Iterator<E> iterator(): Returns an iterator over the elements in this set. The elements are returned in no particular order.
- public boolean remove(Object o): Removes the given element from this set if it is present and return true. If the element is not present in the set, just returns false.
- public int size(): Returns the number of elements in the set.
- public Spliterator<E> spliterator(): Creates a late-binding and fail-fast Spliterator over the elements in this set. This is introduced in Java 8, however I have not used it till now.
- public boolean removeAll(Collection<?> c): HashSet inherits this method from AbstractSet. This method will remove all the elements in the set that are part of the specified collection.
Java HashSet Example
Java HashSet example program showing common usage of HashSet in java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
package com.journaldev.examples; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; public class HashSetExample { public static void main(String[] args) { Set<String> fruits = new HashSet<>(); //add example fruits.add("Apple"); fruits.add("Banana"); //isEmpty example System.out.println("fruits set is empty = "+fruits.isEmpty()); //contains example System.out.println("fruits contains Apple = "+fruits.contains("Apple")); System.out.println("fruits contains Mango = "+fruits.contains("Mango")); //remove example System.out.println("Apple removed from fruits set = "+fruits.remove("Apple")); System.out.println("Mango removed from fruits set = "+fruits.remove("Mango")); //size example System.out.println("fruits set size = "+fruits.size()); //addAll example List<String> list = new ArrayList<>(); list.add("Apple"); list.add("Apple"); list.add("Banana"); list.add("Mango"); System.out.println("fruits set before addAll = "+fruits); System.out.println("list = "+list); fruits.addAll(list); System.out.println("fruits set after addAll = "+fruits); //iterator example Iterator<String> iterator = fruits.iterator(); while(iterator.hasNext()){ System.out.println("Consuming fruit "+iterator.next()); } //removeAll example fruits.add("Orange"); System.out.println("fruits set before removeAll = "+fruits); System.out.println("list = "+list); fruits.removeAll(list); System.out.println("fruits set after removeAll = "+fruits); //clear example fruits.clear(); System.out.println("fruits set is empty = "+fruits.isEmpty()); } } |
Output of above HashSet example program is given below, I am not explaining them since they are self understood.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<span style="color: #008000;"><strong><code> fruits set is empty = false fruits contains Apple = true fruits contains Mango = false Apple removed from fruits set = true Mango removed from fruits set = false fruits set size = 1 fruits set before addAll = [Banana] list = [Apple, Apple, Banana, Mango] fruits set after addAll = [Apple, Mango, Banana] Consuming fruit Apple Consuming fruit Mango Consuming fruit Banana fruits set before removeAll = [Apple, Mango, Orange, Banana] list = [Apple, Apple, Banana, Mango] fruits set after removeAll = [Orange] fruits set is empty = true </code></strong></span> |
Java HashSet ConcurrentModificationException Example
Java HashSet iterator is fail-fast, so it’s methods will throw java.util.ConcurrentModificationException
if Set is structurally modified. Below is a simple example demonstrating this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.journaldev.examples; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetConcurrentModificationExceptionExample { public static void main(String[] args) { Set<String> fruits = new HashSet<>(); //add example fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); Iterator<String> iterator = fruits.iterator(); while(iterator.hasNext()){ String fruit = iterator.next(); System.out.println("Processing "+fruit); //wrong way of removing from Set, can throw java.util.ConcurrentModificationException if("Orange".equals(fruit)) fruits.remove("Orange"); } } } |
I am getting below output and exception when above program is executed.
1 2 3 4 5 6 7 8 9 |
<span style="color: #008000;"><strong><code> Processing Apple Processing Mango Processing Orange Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) at java.util.HashMap$KeyIterator.next(HashMap.java:1453) at com.journaldev.examples.HashSetConcurrentModificationExceptionExample.main(HashSetConcurrentModificationExceptionExample.java:21) </code></strong></span> |
Note that HashSet elements are not guaranteed to be ordered and ConcurrentModificationException is being thrown by iterator.next()
call. So if the “Orange” is the last one in the iterator, you will not get the exception because iterator.hasNext()
will return false and iterator.next()
will not get called.
We should always use Iterator methods for structural modification, as shown in below example code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.journaldev.examples; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetConcurrentModificationExceptionExample { public static void main(String[] args) { Set<String> fruits = new HashSet<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); Iterator<String> iterator = fruits.iterator(); while(iterator.hasNext()){ String fruit = iterator.next(); System.out.println("Processing "+fruit); //correct way of structural modification of Set if("Orange".equals(fruit)) iterator.remove(); } System.out.println("fruits set after iteration = "+fruits); } } |
Above HashSet iterator example will not throw exception and you will get below output.
1 2 3 4 5 6 7 |
<span style="color: #008000;"><strong><code> Processing Apple Processing Mango Processing Orange Processing Banana fruits set after iteration = [Apple, Mango, Banana] </code></strong></span> |
Java HashSet to Array Example
Sometimes we have to convert HashSet to array and vice versa. Below is a simple program showing correct way to convert HashSet to array and then Array to HashSet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package com.journaldev.examples; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class HashSetToArrayExample { public static void main(String[] args) { Set<Integer> ints = new HashSet<>(); for(int i=0; i<10; i++){ ints.add(i); } System.out.println("ints set = "+ints); // set to array example Integer[] intArray = new Integer[ints.size()]; intArray = ints.toArray(intArray); System.out.println("intArray = "+Arrays.toString(intArray)); ints.remove(0);ints.remove(1); System.out.println("intArray = "+Arrays.toString(intArray)); //array to set example ints = new HashSet<>(Arrays.asList(intArray)); System.out.println("ints from array = "+ints); ints.remove(0);ints.remove(1); System.out.println("ints from array after remove = "+ints); System.out.println("intArray = "+Arrays.toString(intArray)); } } |
Output of above HashSet to array example is;
1 2 3 4 5 6 7 8 |
<span style="color: #008000;"><strong><code> ints set = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ints from array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ints from array after remove = [2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] </code></strong></span> |
Java HashSet to List Example
There is not much difference between Set and List, but sometimes we have to convert from Set to List or List to Set. Below is a simple example showing correct way to convert Set to List and then List to Set in java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.journaldev.examples; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class HashSetToListExample { public static void main(String[] args) { Set<String> vowels = new HashSet<>(); vowels.add("a"); vowels.add("e"); vowels.add("i"); //set to list example List<String> vowelsList = new ArrayList<>(vowels); System.out.println("vowels set = "+vowels); System.out.println("vowelsList = "+vowelsList); vowels.add("o"); vowelsList.add("a");vowelsList.add("u"); System.out.println("vowels set = "+vowels); System.out.println("vowelsList = "+vowelsList); //list to set example vowels = new HashSet<>(vowelsList); System.out.println("vowels set = "+vowels); } } |
Output of above java Set to List example program is;
1 2 3 4 5 6 7 |
<span style="color: #008000;"><strong><code> vowels set = [a, e, i] vowelsList = [a, e, i] vowels set = [a, e, i, o] vowelsList = [a, e, i, a, u] vowels set = [a, e, u, i] </code></strong></span> |
Java HashSet equals() and hashCode() methods
HashSet utilise HashMap for storing it’s elements. HashSet works with equals() and hashCode() method to check for duplicate element when you try to add an element. Let’s see what happens if you Set object doesn’t provide equals() method implementation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.journaldev.examples; import java.util.HashSet; import java.util.Set; public class HashSetEqualsMethodImportance { public static void main(String[] args) { Set<Emp> emps = new HashSet<>(); emps.add(new Emp(1,"Pankaj")); emps.add(new Emp(2, "David")); emps.add(new Emp(1, "Pankaj")); System.out.println(emps); } } class Emp { private String name; private int id; public Emp(int i, String n) { this.id = i; this.name = n; } @Override public String toString(){ return "{"+id+","+name+"}"; } } |
When we run above program, we get below output for Set elements.
1 2 3 |
<span style="color: #008000;"><strong><code> [{2,David}, {1,Pankaj}, {1,Pankaj}] </code></strong></span> |
So it looks like we were able to store duplicate elements in the Set. Actually not, it’s happening because Emp class doesn’t define equals() method, so Object class equals() method implementation is used. Object class defines equals() method like below.
1 2 3 4 5 |
public boolean equals(Object obj) { return (this == obj); } |
So when adding a new element, object reference is being checked rather than content. Hence we have objects with duplicate content, however they are having different references. Let’s see what happens when we define hashCode() and equals() methods in the Emp class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package com.journaldev.examples; import java.util.HashSet; import java.util.Set; public class HashSetEqualsMethodImportance { public static void main(String[] args) { Set<Emp> emps = new HashSet<>(); emps.add(new Emp(1,"Pankaj")); emps.add(new Emp(2, "David")); emps.add(new Emp(1, "Pankaj")); System.out.println(emps); Emp e = new Emp(3, "Lisa"); emps.add(e); System.out.println(emps); //set values to make it duplicate e.setId(1); System.out.println(emps); e.setName("Pankaj"); System.out.println(emps); } } class Emp { private String name; private int id; public Emp(int i, String n) { this.setId(i); this.setName(n); } @Override public boolean equals(Object obj){ if(obj == null || !(obj instanceof Emp)) return false; Emp e = (Emp) obj; if(e.getId() == this.getId() && this.getName().equals(e.getName())) return true; return false; } @Override public int hashCode(){ return getId(); } @Override public String toString(){ return "{"+getId()+","+getName()+"}"; } public String getName() { return name; } public int getId() { return id; } public void setName(String name) { this.name = name; } public void setId(int id) { this.id = id; } } |
This time our program produces following output.
1 2 3 4 5 6 |
<span style="color: #008000;"><strong><code> [{1,Pankaj}, {2,David}] [{1,Pankaj}, {2,David}, {3,Lisa}] [{1,Pankaj}, {2,David}, {1,Lisa}] [{1,Pankaj}, {2,David}, {1,Pankaj}] </code></strong></span> |
Notice that HashSet was able to check for duplicate when we tried to add an element. But we can change the object values using setter methods and make it duplicate. It worked because there is no operation done on Set. This is why Immutable objects works better with Set and Map.
That’s all for Java HashSet example tutorial, I hope that all the important things are covered for HashSet in Java. If I have missed anything, please let me know through comments and I will try to add that too.