Java HashSet

Java HashSet is a class in the Java Collections Framework that implements the Set interface. It is an unordered collection of unique elements, where each element is stored only once.

Some important features of Java HashSet include:

  • It does not maintain the order of elements in which they were inserted.
  • It uses hashing to store elements, which allows for fast insertion, deletion, and retrieval of elements.
  • It does not allow duplicate elements. If you try to add a duplicate element, it will simply be ignored.
  • It allows null values.
  • It provides constant-time performance for the basic operations (add, remove, contains, and size) as long as the hash function distributes the elements evenly among the buckets.

Here’s an example of how to create and use a HashSet in Java:

import java.util.HashSet;

public class HashSetExample {
   public static void main(String[] args) {
      HashSet<String> set = new HashSet<>();

      set.add("apple");
      set.add("banana");
      set.add("orange");
      set.add("pear");
      set.add("banana"); // this will be ignored since it's a duplicate

      System.out.println(set); // prints [orange, pear, banana, apple]
      System.out.println(set.contains("apple")); // prints true
      System.out.println(set.size()); // prints 4
   }
}

Difference between List and Set:

In Java, List and Set are two different interfaces in the Collections framework that are used to store a group of objects. The main difference between List and Set is that List allows duplicate elements, while Set does not.

Here are some key differences between List and Set:

  1. Duplicate Elements: List allows duplicate elements while Set doesn’t. In other words, you can add the same element multiple times to a List, but in a Set, duplicates are automatically removed.
  2. Order: List maintains the order of elements in which they were inserted, whereas Set does not maintain the order of elements. In other words, if you add elements to a List in a particular order, the order will be preserved when you retrieve them, but in a Set, the order is not guaranteed.
  3. Index-based Operations: List provides index-based operations, which means you can access elements by their index number, add elements at a particular index, or remove elements at a particular index. Set does not provide such operations, as it does not maintain any order.
  4. Implementation: List is implemented using an ordered sequence, whereas Set is implemented using a hash table.
  5. Performance: List is generally faster than Set when it comes to accessing elements by index, but slower when it comes to checking whether an element is present. Set is faster than List when it comes to checking whether an element is present.

Here’s an example to illustrate the difference between List and Set:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ListSetExample {
   public static void main(String[] args) {
      // Creating a List
      List<String> myList = new ArrayList<>();
      myList.add("apple");
      myList.add("banana");
      myList.add("orange");
      myList.add("apple"); // duplicate element

      // Creating a Set
      Set<String> mySet = new HashSet<>();
      mySet.add("apple");
      mySet.add("banana");
      mySet.add("orange");
      mySet.add("apple"); // duplicate element will be ignored

      System.out.println("List: " + myList); // prints [apple, banana, orange, apple]
      System.out.println("Set: " + mySet); // prints [orange, banana, apple]
   }
}

In the example above, we created a List and a Set with the same elements. When we print the List, we can see that it contains the duplicate element “apple”, but when we print the Set, the duplicate element has been automatically removed.

Hierarchy of HashSet class:

The HashSet class in Java is part of the Java Collections Framework, which is a set of interfaces and classes that provide implementations of commonly used data structures.

The HashSet class is implemented as a hash table, which means that it stores elements in an unordered manner and provides constant-time performance for basic operations like adding, removing, and checking for the presence of an element.

Here is the hierarchy of the HashSet class:

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.AbstractSet<E>
            java.util.HashSet<E>

As you can see from the hierarchy, HashSet is a subclass of AbstractSet which is itself a subclass of AbstractCollection. The AbstractSet and AbstractCollection classes provide some common functionality that can be used by all set and collection implementations.

The HashSet class implements the Set interface, which extends the Collection interface. The Set interface defines the basic operations that a set must support, such as adding, removing, and checking for the presence of an element. The Collection interface provides additional methods for working with collections, such as iterating over the elements and finding the size of the collection.

Overall, the hierarchy of the HashSet class shows how it is related to other classes and interfaces in the Java Collections Framework and provides a foundation for understanding how it works and how it can be used in Java programs.

HashSet class declaration:

Here is the declaration for the HashSet class in Java:

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
    // Constructors
    public HashSet();
    public HashSet(Collection<? extends E> c);
    public HashSet(int initialCapacity);
    public HashSet(int initialCapacity, float loadFactor);

    // Basic operations
    public boolean add(E e);
    public void clear();
    public boolean contains(Object o);
    public boolean isEmpty();
    public Iterator<E> iterator();
    public boolean remove(Object o);
    public int size();

    // Other methods
    public Object clone();
    public boolean equals(Object o);
    public int hashCode();
    public Spliterator<E> spliterator();
    public Stream<E> stream();
    public Stream<E> parallelStream();
    public boolean removeAll(Collection<?> c);
    public boolean retainAll(Collection<?> c);
    public Object[] toArray();
    public <T> T[] toArray(T[] a);
}

The HashSet class is a generic class, indicated by the <E> type parameter. This means that you can create a HashSet to hold any type of object by specifying the object’s type as the type parameter when you create the instance.

The HashSet class extends the AbstractSet class and implements the Set interface, as well as the Cloneable and Serializable interfaces. This means that it inherits some basic set functionality from AbstractSet and implements the required methods from the Set interface.

The class provides several constructors, which allow you to create a HashSet instance with or without specifying an initial capacity and load factor, as well as the option to create a HashSet initialized with the elements of another collection.

The class also provides several methods for performing basic set operations, such as adding, removing, and checking for the presence of an element. In addition, it provides several other methods for working with sets, such as cloning the set, checking for equality with another object, and returning a Spliterator or Stream of the elements in the set.

Overall, the HashSet class provides a powerful and flexible way to store and manipulate collections of unique elements in Java.

Constructors of Java HashSet class:

The HashSet class in Java provides several constructors that allow you to create a new HashSet instance with different options. Here are the available constructors:

  1. HashSet(): Creates an empty HashSet with the default initial capacity (16) and the default load factor (0.75).
  2. HashSet(Collection<? extends E> c): Creates a HashSet initialized with the elements of the specified collection c. The HashSet is created with an initial capacity sufficient to hold the elements in the collection and the default load factor.
  3. HashSet(int initialCapacity): Creates an empty HashSet with the specified initial capacity and the default load factor.
  4. HashSet(int initialCapacity, float loadFactor): Creates an empty HashSet with the specified initial capacity and load factor.

The initialCapacity parameter specifies the initial capacity of the HashSet, which is the number of buckets in the hash table. The loadFactor parameter specifies the load factor of the HashSet, which is a measure of how full the hash table is allowed to get before it is resized.

The load factor is a float value between 0 and 1, and the default load factor is 0.75. A higher load factor means that the hash table can become more densely packed with elements before it is resized, which can improve performance, but also increases memory usage.

By default, HashSet uses the hash code of the elements in the set to distribute them among the buckets in the hash table, but you can also provide your own implementation of the hashCode() and equals() methods for the elements in the set if necessary.

Methods of Java HashSet class:

The HashSet class in Java provides several methods for performing various operations on the set. Here are some of the commonly used methods:

  1. add(E e): Adds the specified element e to the set, if it is not already present. Returns true if the element was added, or false if it was already present.
  2. remove(Object o): Removes the specified element o from the set, if it is present. Returns true if the element was removed, or false if it was not present.
  3. contains(Object o): Returns true if the set contains the specified element o, or false if it does not.
  4. isEmpty(): Returns true if the set is empty, or false if it contains one or more elements.
  5. size(): Returns the number of elements in the set.
  6. clear(): Removes all elements from the set.
  7. iterator(): Returns an iterator over the elements in the set.
  8. toArray(): Returns an array containing all the elements in the set, in no particular order.
  9. addAll(Collection<? extends E> c): Adds all the elements in the specified collection c to the set. Returns true if the set was modified as a result of the operation.
  10. retainAll(Collection<?> c): Removes all elements from the set except those that are also in the specified collection c. Returns true if the set was modified as a result of the operation.
  11. removeAll(Collection<?> c): Removes all elements from the set that are also in the specified collection c. Returns true if the set was modified as a result of the operation.
  12. equals(Object o): Compares the specified object o with the set for equality. Returns true if the object is a set with the same elements as the set, or false otherwise.
  13. hashCode(): Returns the hash code value for the set.
  14. spliterator(): Returns a Spliterator over the elements in the set.
  15. stream(): Returns a sequential Stream over the elements in the set.
  16. parallelStream(): Returns a parallel Stream over the elements in the set.

Overall, the HashSet class provides a comprehensive set of methods for working with sets of unique elements in Java.

Java HashSet Example:

Here’s an example of using the HashSet class in Java:

import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        // create a HashSet of strings
        HashSet<String> set = new HashSet<>();

        // add elements to the set
        set.add("apple");
        set.add("banana");
        set.add("orange");
        set.add("pear");

        // print the set
        System.out.println("Set: " + set);

        // check if an element is present in the set
        System.out.println("Set contains 'banana': " + set.contains("banana"));

        // remove an element from the set
        set.remove("orange");

        // print the set again
        System.out.println("Set after removing 'orange': " + set);

        // iterate over the elements in the set
        System.out.print("Set elements: ");
        for (String element : set) {
            System.out.print(element + " ");
        }
        System.out.println();

        // clear the set
        set.clear();

        // check if the set is empty
        System.out.println("Set is empty: " + set.isEmpty());
    }
}

Output:

Set: [pear, apple, orange, banana]
Set contains 'banana': true
Set after removing 'orange': [pear, apple, banana]
Set elements: pear apple banana 
Set is empty: true

In this example, we first create a HashSet of strings and add some elements to it. We then print the set, check if an element is present, remove an element, and iterate over the elements in the set.

Finally, we clear the set and check if it is empty.

Java HashSet example ignoring duplicate elements:

In Java, HashSet does not allow duplicate elements. When you try to add a duplicate element, it will simply ignore it and not add it to the set. Here’s an example to demonstrate this behavior:

import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        // create a HashSet of integers
        HashSet<Integer> set = new HashSet<>();

        // add some elements to the set
        set.add(10);
        set.add(20);
        set.add(30);
        set.add(20); // this element will be ignored because it's a duplicate

        // print the set
        System.out.println("Set: " + set);
    }
}

Output:

Set: [10, 20, 30]

In this example, we create a HashSet of integers and add some elements to it. Notice that we add the element 20 twice, but it only appears once in the output because HashSet ignores duplicates.

Java HashSet example to remove elements:

Here’s an example of how to remove elements from a HashSet in Java:

import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        // create a HashSet of strings
        HashSet<String> set = new HashSet<>();

        // add some elements to the set
        set.add("apple");
        set.add("banana");
        set.add("orange");
        set.add("pear");

        // print the set
        System.out.println("Set: " + set);

        // remove an element from the set
        set.remove("banana");

        // print the set again
        System.out.println("Set after removing 'banana': " + set);

        // remove all elements that contain the letter 'p'
        set.removeIf(element -> element.contains("p"));

        // print the set again
        System.out.println("Set after removing elements with 'p': " + set);

        // clear the set
        set.clear();

        // check if the set is empty
        System.out.println("Set is empty: " + set.isEmpty());
    }
}

Output:

Set: [pear, apple, orange, banana]
Set after removing 'banana': [pear, apple, orange]
Set after removing elements with 'p': [apple]
Set is empty: true

In this example, we first create a HashSet of strings and add some elements to it. We then remove the element "banana" from the set using the remove() method.

Next, we remove all elements from the set that contain the letter "p" using the removeIf() method and a lambda expression. The removeIf() method removes all elements from the set that satisfy the given predicate.

Finally, we clear the set and check if it is empty using the clear() and isEmpty() methods.

Java HashSet from another Collection:

In Java, you can create a HashSet from another collection using the HashSet(Collection<? extends E> c) constructor. This constructor creates a new HashSet containing all the elements in the specified collection. Here’s an example:

import java.util.ArrayList;
import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        // create an ArrayList of strings
        ArrayList<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("orange");
        list.add("pear");

        // create a HashSet from the ArrayList
        HashSet<String> set = new HashSet<>(list);

        // print the set
        System.out.println("Set: " + set);
    }
}

Output:

Set: [orange, banana, apple, pear]

In this example, we first create an ArrayList of strings and add some elements to it. We then create a HashSet from the ArrayList using the HashSet(Collection<? extends E> c) constructor.

The resulting HashSet contains all the elements from the original ArrayList, but the order may be different because HashSet does not guarantee the order of its elements.

Java HashSet Example: Book

Here’s an example of how to use a HashSet to store a collection of Book objects in Java:

import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        // create some Book objects
        Book book1 = new Book("The Great Gatsby", "F. Scott Fitzgerald");
        Book book2 = new Book("To Kill a Mockingbird", "Harper Lee");
        Book book3 = new Book("1984", "George Orwell");
        Book book4 = new Book("The Catcher in the Rye", "J.D. Salinger");
        Book book5 = new Book("Pride and Prejudice", "Jane Austen");

        // create a HashSet to store the books
        HashSet<Book> bookSet = new HashSet<>();

        // add the books to the HashSet
        bookSet.add(book1);
        bookSet.add(book2);
        bookSet.add(book3);
        bookSet.add(book4);
        bookSet.add(book5);

        // print the HashSet
        System.out.println("Book Set: " + bookSet);

        // remove a book from the HashSet
        bookSet.remove(book4);

        // print the HashSet again
        System.out.println("Book Set after removing 'The Catcher in the Rye': " + bookSet);
    }
}

class Book {
    private String title;
    private String author;

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "\"" + title + "\" by " + author;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Book)) {
            return false;
        }
        Book other = (Book) obj;
        return this.title.equals(other.title) && this.author.equals(other.author);
    }

    @Override
    public int hashCode() {
        return title.hashCode() ^ author.hashCode();
    }
}

Output:

Book Set: ["1984" by George Orwell, "The Catcher in the Rye" by J.D. Salinger, "To Kill a Mockingbird" by Harper Lee, "The Great Gatsby" by F. Scott Fitzgerald, "Pride and Prejudice" by Jane Austen]
Book Set after removing 'The Catcher in the Rye': ["1984" by George Orwell, "To Kill a Mockingbird" by Harper Lee, "The Great Gatsby" by F. Scott Fitzgerald, "Pride and Prejudice" by Jane Austen]

In this example, we create some Book objects and add them to a HashSet using the add() method. We then print the HashSet.

Next, we remove a book from the HashSet using the remove() method. We then print the HashSet again to confirm that the book was removed.

Note that the Book class overrides the equals() and hashCode() methods to ensure that two Book objects with the same title and author are considered equal, and that their hash codes are the same. This is necessary for the HashSet to work correctly.