⚡IntermediateModern Java

Modern Collections API

Use factory methods List.of, Map.of, Set.of, and Map.copyOf for concise immutable collections (Java 9+).

Overview

Java 9 introduced convenient factory methods for creating small, immutable collections without the verbosity of Arrays.asList or new ArrayList. List.of(...), Set.of(...), and Map.of(...) create compact, truly immutable collections. Unlike Collections.unmodifiableList(), these are immutable from construction and cannot hold null values. Java 10 added copyOf methods: List.copyOf(collection) creates an immutable copy of any collection. Map.entry(k, v) and Map.ofEntries() handle larger maps. These APIs reduce boilerplate significantly for test data, configuration, and any collection that should not change.

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.

Loading editor…

Completed: 0 / 4 exercises

Knowledge Check

Question 1 of 4

What happens when you call List.of("a", null, "b")?