Immutable Collections in Java

Naveen AnamaneniUpdated · 4 min read

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

MethodDescription
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, put and clear throw UnsupportedOperationException.
  • No null: null elements, keys or values throw NullPointerException.
  • No duplicates in Set.of or keys of Map.of: they throw IllegalArgumentException.
  • No guaranteed order for Set.of and Map.of: the iteration order may change between runs.

Example: Creating Immutable Collections

Let's see an example of each factory method.

Factories.java▶ Try it
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");
        }
    }
}
Output
[Navika, Srihitha, Advitha]
2 subjects
Navika: 91
Advitha lives in Delhi
Cannot add to List.of

Explanation: 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

CopyOf.java▶ Try it
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());
        }
    }
}
Output
team:     [Navika, Srihitha, Advitha]
snapshot: [Navika, Srihitha]
Set.of duplicate: duplicate element: Java

Explanation: 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

  1. Easy: Create an immutable map of three country codes to country names and print one value.
  2. Harder: Write a Classroom class whose getStudents() method returns a list callers can't modify, and show that the class's own list can still change internally.

Quick check: Immutable collections

4 questions
1What happens when you call add on List.of(...)?
2Can Map.of contain a null value?
3What does Set.of("a", "a") do?
4What does List.copyOf(list) return?

Key Takeaways

Was this tutorial helpful?

Lesson 296 of 482