Immutable Collections in Java
An immutable collection in Java is a collection that cannot be changed after it is created: no elements can be added, removed or replaced. Since Java 9, the factory methods List.of, Set.of and Map.of create such collections in one line, and since Java 10, copyOf makes an unmodifiable copy of an existing collection. Immutable collections are safe to share between methods and threads because nobody can change them. In this lesson, you will learn how to create them and the rules they follow.
Factory Methods
| Method | Description |
|---|---|
List.of(e1, e2, ...) | It creates an unmodifiable list. |
Set.of(e1, e2, ...) | It creates an unmodifiable set; duplicates are an error. |
Map.of(k1, v1, k2, v2, ...) | It creates an unmodifiable map with up to 10 pairs. |
Map.ofEntries(Map.entry(k, v), ...) | It creates an unmodifiable map with any number of pairs. |
List.copyOf(c), Set.copyOf(c), Map.copyOf(m) | It creates an unmodifiable copy of an existing collection. |
stream.toList() | It collects a stream into an unmodifiable list (Java 16). |
Rules of These Collections
- No changes:
add,remove,set,putandclearthrowUnsupportedOperationException. - No
null:nullelements, keys or values throwNullPointerException. - No duplicates in
Set.ofor keys ofMap.of: they throwIllegalArgumentException. - No guaranteed order for
Set.ofandMap.of: the iteration order may change between runs.
Example: Creating Immutable Collections
Let's see an example of each factory method.
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Factories {
public static void main(String[] args) {
List<String> names = List.of("Navika", "Srihitha", "Advitha");
Set<String> subjects = Set.of("Java", "SQL");
Map<String, Integer> marks = Map.of("Navika", 91, "Srihitha", 78);
Map<String, String> cities = Map.ofEntries(
Map.entry("Navika", "Pune"),
Map.entry("Advitha", "Delhi"));
System.out.println(names);
System.out.println(subjects.size() + " subjects");
System.out.println("Navika: " + marks.get("Navika"));
System.out.println("Advitha lives in " + cities.get("Advitha"));
try {
names.add("Anamaneni");
} catch (UnsupportedOperationException e) {
System.out.println("Cannot add to List.of");
}
}
}[Navika, Srihitha, Advitha]
2 subjects
Navika: 91
Advitha lives in Delhi
Cannot add to List.ofExplanation: Each factory method builds a ready-to-use collection in one expression. Trying to add to the list throws UnsupportedOperationException.
Example: copyOf Takes a Snapshot
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class CopyOf {
public static void main(String[] args) {
List<String> team = new ArrayList<>(List.of("Navika", "Srihitha"));
List<String> snapshot = List.copyOf(team);
team.add("Advitha");
System.out.println("team: " + team);
System.out.println("snapshot: " + snapshot);
try {
Set.of("Java", "Java");
} catch (IllegalArgumentException e) {
System.out.println("Set.of duplicate: " + e.getMessage());
}
}
}team: [Navika, Srihitha, Advitha]
snapshot: [Navika, Srihitha]
Set.of duplicate: duplicate element: JavaExplanation: List.copyOf copied the team when it had two members. Adding Advitha later changed team but not the snapshot. Set.of with the same element twice throws IllegalArgumentException.
Why Use Immutable Collections
- Safety: a method can return its internal list without fear that a caller will change it.
- Thread safety: many threads can read the same immutable collection with no locking.
- Clear intent: constants such as
List.of("RED", "GREEN", "BLUE")show they never change. - Memory: the JDK uses compact internal classes for small immutable collections.
Common Mistakes
Expecting Set.of or Map.of to keep insertion order. Their order is unspecified; use LinkedHashSet or LinkedHashMap copies.
Passing a value that might be null. The factory throws; filter null out first.
Assuming the elements are immutable too. A List.of of mutable objects can't change its slots, but the objects inside can still change.
Practice
- Easy: Create an immutable map of three country codes to country names and print one value.
- Harder: Write a
Classroomclass whosegetStudents()method returns a list callers can't modify, and show that the class's own list can still change internally.
Key Takeaways
Was this tutorial helpful?