Modern Collections API
Use factory methods List.of, Map.of, Set.of, and Map.copyOf for concise immutable collections (Java 9+).
Overview
Key Points
- •List.of(1, 2, 3): immutable list, no nulls allowed, no add/remove/set
- •Set.of("a", "b", "c"): immutable set, no duplicates, no nulls
- •Map.of("key1", val1, "key2", val2): up to 10 entries
- •Map.ofEntries(Map.entry("key", val), ...): for 11+ entries
- •List.copyOf(collection): immutable copy, no nulls
- •Map.copyOf(map): immutable copy
- •Unmodifiable vs immutable: Collections.unmodifiableList wraps; List.of is truly immutable
- •The iteration order of Set.of and Map.of is intentionally random (not insertion order)
Examples
List.of, Set.of, Map.of factory methods
import java.util.*;
public class Main {
public static void main(String[] args) {
// Immutable list
List<String> fruits = List.of("apple", "banana", "cherry");
System.out.println(fruits);
System.out.println("Size: " + fruits.size());
System.out.println("Contains banana: " + fruits.contains("banana"));
// Immutable set (no duplicates, random order)
Set<Integer> primes = Set.of(2, 3, 5, 7, 11);
System.out.println("Primes: " + primes);
// Immutable map (up to 10 entries)
Map<String, Integer> scores = Map.of(
"Alice", 95,
"Bob", 87,
"Carol", 92
);
System.out.println("Alice: " + scores.get("Alice"));
// Trying to modify: throws UnsupportedOperationException
try {
fruits.add("date");
} catch (UnsupportedOperationException e) {
System.out.println("Cannot modify immutable list");
}
}
}List.of creates a truly immutable list, not just an unmodifiable view. The elements and size cannot change. Nulls throw NullPointerException at creation time.
Map.ofEntries for larger maps and copyOf
import java.util.*;
public class Main {
public static void main(String[] args) {
// Map.ofEntries: for 11+ entries
Map<String, String> capitals = Map.ofEntries(
Map.entry("France", "Paris"),
Map.entry("Germany", "Berlin"),
Map.entry("Italy", "Rome"),
Map.entry("Spain", "Madrid"),
Map.entry("Portugal", "Lisbon"),
Map.entry("Netherlands", "Amsterdam"),
Map.entry("Belgium", "Brussels")
);
System.out.println("France: " + capitals.get("France"));
System.out.println("Size: " + capitals.size());
// copyOf: immutable copy of an existing collection
List<String> mutable = new ArrayList<>(List.of("a", "b", "c"));
mutable.add("d");
List<String> snapshot = List.copyOf(mutable); // immutable copy at this moment
mutable.add("e"); // does not affect snapshot
System.out.println("Mutable: " + mutable);
System.out.println("Snapshot: " + snapshot);
// Map.copyOf
Map<String, Integer> original = new HashMap<>();
original.put("x", 1);
original.put("y", 2);
Map<String, Integer> copy = Map.copyOf(original);
original.put("z", 3); // does not affect copy
System.out.println("Copy: " + copy);
}
}copyOf creates an independent immutable snapshot. Changes to the original collection do not affect the copy.
Practical: combining immutable collections with streams
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
// Immutable collections as source data
var allowed = Set.of("admin", "editor", "viewer");
var users = List.of(
Map.entry("Alice", "admin"),
Map.entry("Bob", "unknown"),
Map.entry("Carol", "editor"),
Map.entry("Dave", "viewer"),
Map.entry("Eve", "hacker")
);
// Filter users with valid roles
List<String> validUsers = users.stream()
.filter(e -> allowed.contains(e.getValue()))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
System.out.println("Valid users: " + validUsers);
// Group by role
Map<String, List<String>> byRole = users.stream()
.filter(e -> allowed.contains(e.getValue()))
.collect(Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())
));
byRole.forEach((role, names) -> System.out.println(role + ": " + names));
}
}Immutable collections are excellent as lookup sets (Set.of for membership checks) and as configuration maps. They can be used as keys in other maps because their hashCode is stable.
Common Mistakes
- Passing null to List.of(), Set.of(), or Map.of(). These throw NullPointerException immediately. Use a mutable collection if you need null support
- Adding duplicate values to Set.of(). Set.of("a", "a") throws IllegalArgumentException at runtime, not silently deduplicates
- Expecting insertion-order iteration from Set.of() or Map.of(). These do not preserve order. Use LinkedHashSet or LinkedHashMap if order matters
Note
Use List.of for any collection that should never be modified: test data, configuration values, fixed lookup tables. It is faster than ArrayList for iteration and searching, and safer because accidental modification throws immediately.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 4 exercises
Knowledge Check
Question 1 of 4