An In-depth Guide to the Set Interface

Hemant BesraHemant Besra
7 min read

Set Interface

When working with collections in Java, you'll often come across scenarios where you need to manage a group of elements with unique values. This is where the Set interface comes into play. In this comprehensive guide, we will explore the Set interface, its implementations, and how to use it effectively in your Java applications.

The Set interface is a part of the Java Collections Framework and is designed to represent a collection of elements where each element is unique. Unlike a List, which allows duplicate values and maintains elements in a specific order, a Set enforces uniqueness but does not guarantee any specific order of elements. The Set interface is defined in the java.util package.

Common Implementations of Set

When working with collections in Java, you'll often come across scenarios where you need to manage a group of elements with unique values. This is where the Set interface comes into play. In this comprehensive guide, we will explore the Set interface, its implementations, and how to use it effectively in your Java applications. Java offers several concrete implementations of the Set interface, each with its own characteristics. Let's explore some of the most commonly used ones:

  1. HashSet:

    • Uniqueness: HashSet enforces uniqueness; it automatically prevents duplicate elements.

    • No Specific Order: Elements in a HashSet are not stored in any specific order. They are organized based on their hash code values, making the order unpredictable.

    • Efficient Operations: HashSet offers constant-time (O(1)) average time complexity for basic operations like add, remove, and contains.

    • Null Values: HashSet allows one null element to be stored.

    • Not Synchronized: HashSet is not synchronized by default, meaning it is not thread-safe. If thread safety is required, consider using Collections.synchronizedSet().

    • Iterating: You can iterate through the elements of a HashSet using an iterator or enhanced for loop. Keep in mind that the order of iteration is not guaranteed.

    • HashSet is one of the most popular Set implementations. It uses a hash table to store elements, providing fast insertion, deletion, and retrieval operations. However, it does not maintain any specific order of elements.

  2. LinkedHashSet:

    • Uniqueness: LinkedHashSet enforces uniqueness among elements; duplicates are not allowed.

    • Order Preserving: It maintains the order of insertion. When you iterate over it, elements are returned in the order they were added.

    • Efficient Operations: LinkedHashSet provides efficient constant-time (O(1)) average time complexity for basic operations like add, remove, and contains.

    • Null Values: LinkedHashSet allows one null element to be stored.

    • Not Synchronized: LinkedHashSet is not thread-safe by default. If thread safety is needed, consider using Collections.synchronizedSet().

    • Iterating: Iterating over a LinkedHashSet guarantees the order of elements, which is based on insertion order.

    • LinkedHashSet is similar to HashSet but adds a linked list to maintain the order of insertion. Elements in a LinkedHashSet are stored in the order in which they were added, making it suitable for scenarios where you need to iterate through elements in a predictable order.

  3. TreeSet:

    • Ordered: TreeSet maintains the elements in a specific order, making it suitable for scenarios where you need elements sorted.

    • Uniqueness: TreeSet enforces uniqueness; it automatically prevents duplicate elements.

    • Efficient Operations: TreeSet offers efficient log-time (O(log n)) time complexity for basic operations like add, remove, and contains.

    • Null Values: TreeSet does not allow null elements. If you need to store null, consider using a different Set implementation like HashSet.

    • TreeSet is a NavigableSet that uses a Red-Black tree to store elements. It offers ordered traversal of elements, either in natural order or according to a specified comparator. TreeSet is useful when you need to work with sorted sets of elements.

    • Creating a TreeSet with a Custom Comparator:

        import java.util.Comparator;
        import java.util.Set;
        import java.util.TreeSet;
        public class CustomComparatorExample {
            public static void main(String[] args) {
                // Create a TreeSet of Strings sorted by length
                Comparator<String> lengthComparator = Comparator.comparing(String::length);
                Set<String> treeSet = new TreeSet<>(lengthComparator);
      
                // Adding elements to the TreeSet
                treeSet.add("Apple");
                treeSet.add("Banana");
                treeSet.add("Cherry");
      
                // Printing the TreeSet
                System.out.println(treeSet); // Output: [Apple, Cherry, Banana]
            }
        }
      
    • Iterating Over a TreeSet:

      You can iterate over the elements of a TreeSet using an enhanced for loop or an iterator. Elements will be returned in the sorted order.

    • Navigating TreeSet:

      TreeSet provides methods like first(), last(), ceiling(), floor(), higher(), and lower() to navigate through the elements efficiently.

        import java.util.Set;
        import java.util.TreeSet;
      
        public class TreeSetNavigationExample {
            public static void main(String[] args) {
                // Create a TreeSet of Integers
                Set<Integer> treeSet = new TreeSet<>();
      
                // Adding elements to the TreeSet
                treeSet.add(5);
                treeSet.add(2);
                treeSet.add(8);
                treeSet.add(3);
                treeSet.add(1);
      
                // Using navigation methods
                System.out.println("First element: " + treeSet.first());   // Smallest element
                System.out.println("Last element: " + treeSet.last());     // Largest element
                System.out.println("Ceiling of 4: " + treeSet.ceiling(4)); // Smallest element >= 4
                System.out.println("Floor of 6: " + treeSet.floor(6));     // Largest element <= 6
                System.out.println("Higher than 2: " + treeSet.higher(2));  // Smallest element > 2
                System.out.println("Lower than 5: " + treeSet.lower(5));    // Largest element < 5
            }
        }
      
        /*
        first() returns the smallest (first) element in the set.
        last() returns the largest (last) element in the set.
        ceiling(4) returns the smallest element that is greater than or equal to 4.
        floor(6) returns the largest element that is less than or equal to 6.
        higher(2) returns the smallest element that is strictly greater than 2.
        lower(5) returns the largest element that is strictly less than 5.
        */
        /*
        First element: 1
        Last element: 8
        Ceiling of 4: 5
        Floor of 6: 5
        Higher than 2: 3
        Lower than 5: 3
        */
      

Set Operations

Sets support various set operations that you might find useful when working with collections of unique elements:

  1. Union: Combines two sets to create a new set containing all unique elements from both sets.

  2. Intersection: Produces a new set containing only the elements that exist in both input sets.

  3. Difference: Creates a new set containing elements that are in one set but not in another.

  4. Subset and Superset: You can check if one set is a subset or superset of another set.

import java.util.HashSet;
import java.util.Set;

public class SetOperationsExample {
    public static void main(String[] args) {
        // Creating two sets
        Set<Integer> setA = new HashSet<>();
        Set<Integer> setB = new HashSet<>();

        // Adding elements to setA
        setA.add(1);
        setA.add(2);
        setA.add(3);

        // Adding elements to setB
        setB.add(3);
        setB.add(4);
        setB.add(5);

        // Union: Combines two sets to create a new set containing all unique elements
        Set<Integer> unionSet = new HashSet<>(setA);
        unionSet.addAll(setB);
        System.out.println("Union Set: " + unionSet); // Output: Union Set: [1, 2, 3, 4, 5]

        // Intersection: Produces a new set containing elements that exist in both input sets
        Set<Integer> intersectionSet = new HashSet<>(setA);
        intersectionSet.retainAll(setB);
        System.out.println("Intersection Set: " + intersectionSet); // Output: Intersection Set: [3]

        // Difference: Creates a new set containing elements in setA but not in setB
        Set<Integer> differenceSet = new HashSet<>(setA);
        differenceSet.removeAll(setB);
        System.out.println("Difference Set (A - B): " + differenceSet); // Output: Difference Set (A - B): [1, 2]

        // Subset and Superset: Checking if one set is a subset or superset of another
        boolean isSubset = setA.containsAll(setB);
        boolean isSuperset = setB.containsAll(setA);

        System.out.println("Is setA a subset of setB? " + isSubset); // Output: Is setA a subset of setB? false
        System.out.println("Is setB a superset of setA? " + isSuperset); // Output: Is setB a superset of setA? false
    }
}

Below is a code where I have used some methods of Set.

package com.hk.basic;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetDemo {

    Set<Integer> hashSet;
    Set<Integer> linkedHashSet;
    Set<Integer> treeSet;

    public static void main(String[] args) {
        SetDemo demo = new SetDemo();

        //create set
        demo.hashSet = new HashSet<>();
        demo.linkedHashSet = new LinkedHashSet<>();
        demo.treeSet = new TreeSet<>();

        demo.addElement(demo);
        System.out.println("after adding\n"+demo);

        //remove element
        demo.hashSet.remove(3);        

        //check element
        System.out.println(demo.hashSet.contains(3));
        System.out.println(demo.linkedHashSet.contains(2));        

        System.out.println("after removing\n"+demo);        

        iterateSet(demo.hashSet);
        iterateSet(demo.linkedHashSet);
        iterateSet(demo.treeSet);


    }

    public static void iterateSet(Set<Integer> set) {
        System.out.println();
        for (Integer fruit : set) {
            System.out.print(fruit);
        }

        // Using an iterator
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()) {
            Integer fruit = iterator.next();
            System.out.print(fruit);
        }
    }

    public  void addElement(SetDemo demo) {
        //add element
        demo.hashSet.add(1);
        demo.hashSet.add(1);
        demo.hashSet.add(2);
        demo.hashSet.add(3);
        demo.hashSet.add(3);

        demo.linkedHashSet.add(1);
        demo.linkedHashSet.add(1);
        demo.linkedHashSet.add(2);
        demo.linkedHashSet.add(3);
        demo.linkedHashSet.add(3);

        demo.treeSet.add(1);
        demo.treeSet.add(1);
        demo.treeSet.add(2);
        demo.treeSet.add(3);
        demo.treeSet.add(3);
    }

    @Override
    public String toString() {
        return "hashSet=" + hashSet + "\nlinkedHashSet=" + linkedHashSet + "\ntreeSet=" + treeSet ;
    }
}

This is all about the Set interface, in the next blog we will learn about other implementations of the Collection Interface.

Click here to read about the Java collection framework.

Click here to read about the List Interface.

Click here to read about my most popular blog i.e. binary search.

0
Subscribe to my newsletter

Read articles from Hemant Besra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hemant Besra
Hemant Besra

Experienced Full Stack Java developer. Have Strong Experience in JSP/Servlet, JSF, Jasper Report, Spring Framework, hibernate, Angular 5+, Microservices. Experienced in Front-end technologies such as HTML, CSS, JavaScript, angular 6+, AJAX, JSON, and XML. Strong Hands-on experience on working with Reactive Forms to build form-based application in Angular 6+.