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:
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
, andcontains
.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.
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
, andcontains
.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.
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
, andcontains
.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()
, andlower()
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:
Union: Combines two sets to create a new set containing all unique elements from both sets.
Intersection: Produces a new set containing only the elements that exist in both input sets.
Difference: Creates a new set containing elements that are in one set but not in another.
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.