Java 9 – Factory Methods for Immutable Set: Set.of() with Strings and Beans
Java 9 introduced Set.of(), a convenient factory method for creating immutable sets in one line. Before Java 9, you had to create a HashSet, add elements individually, and then wrap it with Collections.unmodifiableSet(). The new factory method is concise, null-safe, and produces a truly unmodifiable set.
This tutorial covers two practical examples: using Set.of() with plain strings and with custom Java Bean objects.
What is Set.of()?
Set.of(E... elements) is a varargs static factory method on the Set interface. It was defined in JEP 269 and is available from Java 9 onwards. The resulting set has these properties:
- Truly immutable — add, remove, and clear throw
UnsupportedOperationException - No null elements — throws
NullPointerExceptionif null is passed - No duplicate elements — throws
IllegalArgumentExceptionfor duplicates - No guaranteed iteration order (unlike
LinkedHashSet)
Example 1: Set.of() with Strings
package com.java9r;
import java.util.Set;
/**
* Java 9 – Immutable Set.of() with String elements.
*/
public class Java9ImmutableSetString {
public static void main(String[] args) {
// Create an immutable set of programming languages
var languages = Set.of(
"Java", "Python", "JavaScript", "Go", "Kotlin", "Rust", "C#"
);
System.out.println("Languages: " + languages);
System.out.println("Size: " + languages.size());
System.out.println("Contains 'Java': " + languages.contains("Java"));
System.out.println("Contains 'PHP': " + languages.contains("PHP"));
// Iterate using for-each
System.out.println("\nAll languages:");
languages.stream().sorted().forEach(l -> System.out.println(" - " + l));
// Filter using Stream API
System.out.println("\nLanguages with 'a' in name:");
languages.stream()
.filter(l -> l.toLowerCase().contains("a"))
.sorted()
.forEach(l -> System.out.println(" " + l));
// Convert to mutable when you need to add/remove
var mutableSet = new java.util.HashSet<>(languages);
mutableSet.add("TypeScript");
System.out.println("\nMutable set size: " + mutableSet.size());
// Immutability tests
System.out.println("\nImmutability checks:");
try { languages.add("Scala"); }
catch (UnsupportedOperationException e) {
System.out.println("add() blocked: " + e.getClass().getSimpleName());
}
try { languages.remove("Java"); }
catch (UnsupportedOperationException e) {
System.out.println("remove() blocked: " + e.getClass().getSimpleName());
}
// Duplicate key check
System.out.println("\nDuplicate key check:");
try { Set.of("A", "B", "A"); } // same element twice
catch (IllegalArgumentException e) {
System.out.println("Duplicate throws: " + e.getClass().getSimpleName());
}
// Null check
try { Set.of("A", null); }
catch (NullPointerException e) {
System.out.println("Null throws: " + e.getClass().getSimpleName());
}
}
}
Expected Output
Languages: [Go, Kotlin, Rust, Java, JavaScript, Python, C#]
Size: 7
Contains 'Java': true
Contains 'PHP': false
All languages:
- C#
- Go
- Java
- JavaScript
- Kotlin
- Python
- Rust
Languages with 'a' in name:
Java
JavaScript
Mutable set size: 8
Immutability checks:
add() blocked: UnsupportedOperationException
remove() blocked: UnsupportedOperationException
Duplicate key check:
Duplicate throws: IllegalArgumentException
Null throws: NullPointerException
Example 2: Set.of() with Java Bean Objects
package com.java9r;
import java.util.Objects;
import java.util.Set;
class Product {
private final String id;
private final String name;
private final double price;
public Product(String id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
// equals() and hashCode() are required for Set to detect duplicates
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product)) return false;
return Objects.equals(id, ((Product) o).id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return String.format("Product{id='%s', name='%s', price=%.2f}", id, name, price);
}
}
/**
* Java 9 – Immutable Set.of() with Bean objects.
* IMPORTANT: The bean must implement equals() and hashCode()
* for duplicate detection to work correctly.
*/
public class Java9ImmutableSetBean {
public static void main(String[] args) {
var catalog = Set.of(
new Product("P001", "Laptop", 75000.00),
new Product("P002", "Smartphone", 25000.00),
new Product("P003", "Tablet", 35000.00),
new Product("P004", "Monitor", 15000.00),
new Product("P005", "Keyboard", 2500.00)
);
System.out.println("=== Product Catalog ===");
catalog.stream()
.sorted((a, b) -> a.getId().compareTo(b.getId()))
.forEach(System.out::println);
System.out.println("\nTotal products: " + catalog.size());
// Check if a specific product is in the set
var search = new Product("P003", "Tablet", 0); // price ignored in equals()
System.out.println("\nSearching for P003: " + catalog.contains(search));
// Find most expensive
catalog.stream()
.max((a, b) -> Double.compare(a.getPrice(), b.getPrice()))
.ifPresent(p -> System.out.println("Most expensive: " + p.getName()
+ " (Rs. " + p.getPrice() + ")"));
// Find products under Rs. 30,000
System.out.println("\nAffordable products (under Rs. 30,000):");
catalog.stream()
.filter(p -> p.getPrice() < 30_000)
.sorted((a, b) -> Double.compare(a.getPrice(), b.getPrice()))
.forEach(p ->
System.out.printf(" %-12s Rs. %,.0f%n", p.getName(), p.getPrice()));
}
}
Expected Output
=== Product Catalog ===
Product{id='P001', name='Laptop', price=75000.00}
Product{id='P002', name='Smartphone', price=25000.00}
Product{id='P003', name='Tablet', price=35000.00}
Product{id='P004', name='Monitor', price=15000.00}
Product{id='P005', name='Keyboard', price=2500.00}
Total products: 5
Searching for P003: true
Most expensive: Laptop (Rs. 75000.0)
Affordable products (under Rs. 30,000):
Keyboard Rs. 2,500
Monitor Rs. 15,000
Smartphone Rs. 25,000
Set.of() vs Other Set Creation Methods
| Method | Immutable? | Null allowed? | Duplicates? | Order |
|---|---|---|---|---|
| Set.of() | Yes | No (NPE) | No (IAE) | Unspecified |
| new HashSet<>() | No | Yes (one null) | Silently ignored | Unspecified |
| new LinkedHashSet<>() | No | Yes (one null) | Silently ignored | Insertion order |
| Collections.unmodifiableSet() | Yes (view) | Depends on backing | Silently ignored | Depends on backing |
Summary
Set.of() in Java 9 is the cleanest way to create an immutable set of unique elements. It is concise, rejects nulls and duplicates at construction time, and produces a completely unmodifiable set. When using Set.of() with custom Bean objects, always ensure equals() and hashCode() are implemented — otherwise duplicate detection will not work correctly. For a modifiable copy, wrap it in new HashSet<>().
Comments