The Java HashSet class is utilized for constructing a collection that employs a hash table to store elements. This class inherits characteristics from the AbstractSet class and implements the Set interface.
The important points about Java HashSet class are:
- HashSet stores the elements by using a mechanism called hashing.
- HashSet contains unique elements only.
- HashSet allows null value.
- HashSet class is non synchronized.
- HashSet doesn't maintain the insertion order. Here, elements are inserted on the basis of their hashcode.
- HashSet is the best approach for search operations.
- The initial default capacity of HashSet is 16, and the load factor is 0.75.
Difference Between List and Set
A list is capable of holding duplicate elements, while a Set only allows unique elements to be stored within it.
Hierarchy of HashSet class
The HashSet class is an extension of the AbstractSet class, which in turn implements the Set interface. This interface inherits the Collection and Iterable interfaces in a hierarchical manner.
In Java, a HashSet is a class for storing a unique collection of elements, implementing the Set interface. This collection does not permit duplicates. The internal storage mechanism of a HashSet involves utilizing a HashMap. Here, each element within the HashSet is maintained as a key in the HashMap and is linked with a placeholder value. By leveraging the hash code of an element, a HashSet determines the element's placement within the hash table it utilizes internally. Consequently, this approach facilitates constant-time execution for fundamental operations such as addition, removal, and presence check, provided an efficient hash function is in place.
A significant characteristic of a HashSet is its capability to store elements in an unordered manner. This implies that the sequence of adding elements to a HashSet is not retained during iteration. To preserve the order of insertion, a LinkedHashSet can be employed. LinkedHashSet, an alternative implementation of the Set interface, maintains the sequence in which elements are inserted.
The HashSet class does not accept null elements, although it can hold a solitary null value. When trying to insert a duplicate element into a HashSet, the add method will result in false and the HashSet's content will stay unaltered. HashSet offers functionalities for set operations such as union, intersection, and difference. Unlike certain collection classes, HashSet lacks synchronization, making it non-thread-safe. To obtain a synchronized (thread-safe) variant of a HashSet, the Collections.synchronizedSet method can be utilized to encapsulate the HashSet within a synchronized set.
HashSet Class Declaration
Let's examine the declaration of the java.util.HashSet class.
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
Constructors of Java HashSet Class
| SN | Constructor | Description |
|---|---|---|
| 1) | HashSet() | It is used to construct a default HashSet. |
| 2) | HashSet(int capacity) | It is used to initialize the capacity of the hash set to the given integer value capacity. The capacity grows automatically as elements are added to the HashSet. |
| 3) | HashSet(int capacity, float loadFactor) | It is used to initialize the capacity of the hash set to the given integer value capacity and the specified load factor. |
| 4) | HashSet(Collection<? extends E> c) | It is used to initialize the hash set by using the elements of the collection c. |
Methods of Java HashSet Class
| SN | Modifier & Type | Method | Description |
|---|---|---|---|
| 1) | boolean | add(E e) | It is used to add the specified element to this set if it is not already present. |
| 2) | void | clear() | It is used to remove all of the elements from the set. |
| 3) | object | clone() | It is used to return a shallow copy of this HashSet instance: the elements themselves are not cloned. |
| 4) | boolean | contains(Object o) | It is used to return true if this set contains the specified element. |
| 5) | boolean | isEmpty() | It is used to return true if this set contains no elements. |
| 6) | Iterator<E> | iterator() | It is used to return an iterator over the elements in this set. |
| 7) | boolean | remove(Object o) | It is used to remove the specified element from this set if it is present. |
| 8) | int | size() | It is used to return the number of elements in the set. |
| 9) | Spliterator<E> | spliterator() | It is used to create a late-binding and fail-fast Spliterator over the elements in the set. |
Java HashSet Example
Let's explore a straightforward illustration of how a HashSet works. It's important to observe that the elements are iterated in an unsorted manner within this collection.
HashSetExample1.java
import java.util.*;
class Main{
public static void main(String args[]){
//Creating HashSet and adding elements
HashSet<String> set=new HashSet();
set.add("One");
set.add("Two");
set.add("Three");
set.add("Four");
set.add("Five");
Iterator<String> i=set.iterator();
while(i.hasNext())
{
System.out.println(i.next());
}
}
}
Output:
Five
One
Four
Two
Three
Ignoring Duplicate Elements
In this illustration, it is evident that HashSet does not permit the inclusion of duplicate elements.
Example
import java.util.*;
class Main{
public static void main(String args[]){
//Creating HashSet and adding elements
HashSet<String> set=new HashSet<String>();
set.add("Ravi");
set.add("Vijay");
set.add("Ravi");
set.add("Ajay");
//Traversing elements
Iterator<String> itr=set.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}
}
Output:
Ajay
Vijay
Ravi
Removing Elements
Here, we see different ways to remove an element.
Example
import java.util.*;
class Main{
public static void main(String args[]){
HashSet<String> set=new HashSet<String>();
set.add("Ravi");
set.add("Vijay");
set.add("Arun");
set.add("Sumit");
System.out.println("An initial list of elements: "+set);
//Removing specific element from HashSet
set.remove("Ravi");
System.out.println("After invoking remove(object) method: "+set);
HashSet<String> set1=new HashSet<String>();
set1.add("Ajay");
set1.add("Gaurav");
set.addAll(set1);
System.out.println("Updated List: "+set);
//Removing all the new elements from HashSet
set.removeAll(set1);
System.out.println("After invoking removeAll() method: "+set);
//Removing elements on the basis of specified condition
set.removeIf(str->str.contains("Vijay"));
System.out.println("After invoking removeIf() method: "+set);
//Removing all the elements available in the set
set.clear();
System.out.println("After invoking clear() method: "+set);
}
}
Output:
An initial list of elements: [Vijay, Ravi, Arun, Sumit]
After invoking remove(object) method: [Vijay, Arun, Sumit]
Updated List: [Vijay, Arun, Gaurav, Sumit, Ajay]
After invoking removeAll() method: [Vijay, Arun, Sumit]
After invoking removeIf() method: [Arun, Sumit]
After invoking clear() method: []
Java HashSet from another Collection
HashSetExample4.java
import java.util.*;
class Main{
public static void main(String args[]){
ArrayList<String> list=new ArrayList<String>();
list.add("Ravi");
list.add("Vijay");
list.add("Ajay");
HashSet<String> set=new HashSet(list);
set.add("Gaurav");
Iterator<String> i=set.iterator();
while(i.hasNext())
{
System.out.println(i.next());
}
}
}
Output:
Vijay
Ravi
Gaurav
Ajay
Java HashSet Example: Book
Let's explore an illustration of using a HashSet by adding various books to the set and subsequently displaying all the books present in the set.
Example
import java.util.*;
class Book {
int id;
String name,author,publisher;
int quantity;
public Book(int id, String name, String author, String publisher, int quantity) {
this.id = id;
this.name = name;
this.author = author;
this.publisher = publisher;
this.quantity = quantity;
}
}
public class Main {
public static void main(String[] args) {
HashSet<Book> set=new HashSet<Book>();
//Creating Books
Book b1=new Book(101,"Let us C","Yashwant Kanetkar","BPB",8);
Book b2=new Book(102,"Data Communications & Networking","Forouzan","Mc Graw Hill",4);
Book b3=new Book(103,"Operating System","Galvin","Wiley",6);
//Adding Books to HashSet
set.add(b1);
set.add(b2);
set.add(b3);
//Traversing HashSet
for(Book b:set){
System.out.println(b.id+" "+b.name+" "+b.author+" "+b.publisher+" "+b.quantity);
}
}
}
Output:
101 Let us C Yashwant Kanetkar BPB 8
102 Data Communications & Networking Forouzan Mc Graw Hill 4
103 Operating System Galvin Wiley 6
Advantages of Using Java HashSet
Java HashSets provide multiple benefits that render them valuable for specific programming scenarios. A significant advantage is their capability to hold unique elements, which is ideal for situations that prohibit duplicates. This functionality streamlines the management of a set of different values, as the HashSet inherently manages the identification and elimination of duplicates.
One more benefit of utilizing Java HashSets is their effectiveness in executing fundamental operations like insertion, deletion, and element existence verification. The efficiency of HashSet is accomplished through the utilization of a hash table to save its elements, enabling these operations to run with a time complexity of O(1) on average, provided that a well-designed hash function is in place.
Java HashSets offer various set operations like union, intersection, and difference, which are beneficial for handling sets of elements. These operations are executed effectively, making HashSet a practical option for tasks requiring set manipulation.
The internal structure of a HashSet is well-suited for situations where quick search operations are crucial. By employing a hashing technique to establish the placement of elements within its hash table, HashSet can efficiently pinpoint elements according to their hash codes. This results in rapid retrieval of elements from the set.
In general, Java HashSets provide a blend of effectiveness, ease, and adaptability that render them a beneficial asset for handling sets of distinct elements in Java programs.
Disadvantages of Java HashSet in Java
Although Java HashSets provide numerous benefits, they also have certain drawbacks that should be taken into account. One of the disadvantages is the lack of a guarantee for the order of elements within a HashSet. Consequently, the sequence in which elements are inserted into the HashSet is not retained during iteration. In cases where a specific order is necessary, an alternative data structure like a LinkedHashSet should be considered as it preserves the order in which elements are inserted.
One more drawback of Java HashSets is their restriction on duplicate elements. Although this is commonly a preferred characteristic, there are situations where you might require storing multiple instances of an element. In such scenarios, HashSet is inadequate, and you might have to opt for an alternative data structure that permits duplicates, like an ArrayList or LinkedList.
HashSet incurs a greater memory overhead in contrast to certain alternative data structures. The reason behind this is the utilization of a hash table by HashSet to house its elements, necessitating extra memory allocation for upholding the hash table's configuration. If there are apprehensions regarding memory consumption, it might be prudent to explore employing an alternative data structure with a diminished memory overhead.
Moreover, HashSet lacks synchronization, making it unsuitable for concurrent operations. In scenarios where HashSet is utilized in a multi-threaded setting, manual synchronization is necessary to prevent simultaneous modifications, which can introduce intricacy to your codebase and potentially degrade performance.
In general, Java HashSets come with numerous benefits, including effective performance and automatic identification of duplicates. However, it is crucial to acknowledge the limitations of HashSets when deciding on the appropriate data structure for your specific application.
Let's explore the concept of Java HashSet in Java through an illustrative code snippet.
Example 1:
Filename: HashSetExample.java
// Java program to demonstrate the features and functionalities of Java HashSet.
import java.util.HashSet;
import java.util.Iterator;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && name.equals(person.name);
}
@Override
public int hashCode() {
return name.hashCode() + age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class HashSetExample {
public static void main(String[] args) {
// Create a HashSet
HashSet<String> hashSet = new HashSet<>();
// Add elements to the HashSet
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Orange");
// Display the HashSet
System.out.println("HashSet: " + hashSet);
// Iterate over the HashSet using an Iterator
System.out.print("Iterating over the HashSet using Iterator: ");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.print(element + " ");
}
System.out.println();
// Convert HashSet to an array
String[] array = hashSet.toArray(new String[0]);
System.out.print("HashSet converted to array: ");
for (String element : array) {
System.out.print(element + " ");
}
System.out.println();
// Create a HashSet of custom objects
HashSet<Person> personSet = new HashSet<>();
personSet.add(new Person("Alice", 30));
personSet.add(new Person("Bob", 25));
// Display the HashSet of custom objects
System.out.println("HashSet of custom objects: " + personSet);
}
}
Output:
HashSet: [Apple, Orange, Banana]
Iterating over the HashSet using Iterator: Apple Orange Banana
HashSet converted to array: Apple Orange Banana
HashSet of custom objects: [Person{name='Alice', age=30}, Person{name='Bob', age=25}]