Immutable Set in Java
Problem
Set.of() (Java 9+) builds a Set that can never be modified after creation, the same immutability guarantee List.of() provides for lists.
Create a Set of elements that can never be added to or removed from after it's built.
Java Program
import java.util.Set;
public class ImmutableSetDemo {
public static void main(String[] args) {
Set<String> fruits = Set.of("Apple", "Banana", "Cherry");
System.out.println("Contains Banana: " + fruits.contains("Banana"));
// Set.of("Apple", "Apple") would throw IllegalArgumentException — duplicates aren't allowed
}
}Output
Core Logic
Building the set through the Set.of() factory method returns an immutable implementation, the same idea as List.of() but for sets.
- 1
Set.of("Apple", "Banana", "Cherry")returns a Set already populated with those three elements. - 2Reading from it —
contains(), iteration, size — works exactly like any other Set. - 3Calling a mutating method like
.add(...)on it throwsUnsupportedOperationException, the same as List.of(). - 4
Set.of()does not iterate elements in insertion order — the order elements print in isn't guaranteed, unlikeList.of(), which does preserve the order given.
Set.of("Apple", "Banana", "Cherry").contains("Banana") returns true, read like any normal Set.Key Point: Set.of() rejects duplicate arguments outright — passing the same element twice throws IllegalArgumentException at the moment the set is built, rather than silently keeping just one copy the way a mutable HashSet's add() would.
Why: Set.of() stores each of the n given elements once, after checking for duplicates among them.
Key Concepts
Approach 2: Java 8
import java.util.Set;
import java.util.stream.Collectors;
public class ImmutableSetStream {
public static void main(String[] args) {
Set<String> source = Set.of("Apple", "Banana", "Cherry");
// Transforms each element, then collects the result into a genuinely immutable set
Set<String> upper = source.stream()
.map(String::toUpperCase)
.collect(Collectors.toUnmodifiableSet());
System.out.println("Contains BANANA: " + upper.contains("BANANA"));
// upper.add("DATE") here would throw UnsupportedOperationException
}
}
Output
Core Logic
Collectors.toUnmodifiableSet() builds an immutable set from a stream pipeline directly, for the common case where the elements are derived from existing data rather than being known ahead of time as literals.
- 1
source.stream()opens a stream over an existing mutable set. - 2
.map(String::toUpperCase)transforms each element — somethingSet.of()can't express, since it just takes fixed elements directly. - 3
.collect(Collectors.toUnmodifiableSet())gathers the transformed elements into a genuinely immutable set, the sameUnsupportedOperationException-on-mutation guaranteeSet.of()provides. - 4Calling
.add(...)on the result throws exactly as it would on aSet.of()result.
{Apple, Banana, Cherry} and collecting produces the immutable set {APPLE, BANANA, CHERRY}, so contains("BANANA") returns true.Key Point: Set.of() is for elements already known as literals; Collectors.toUnmodifiableSet() is for the more common real-world case — an immutable set built from data that was transformed or derived from another collection first.
Why: map() visits each of the n source elements once, and collect() builds a new immutable set to hold the transformed elements.