A list that rejects add calls may still change when another reference mutates its backing list. This small example shows why a read-only view and a copied list serve different purposes.

What you will build

See the difference between a snapshot and a read-only view by mutating the original list after creating both.

Before you start

JDK 11 or later. No additional libraries are needed.

1. Create ListSnapshots.java

import java.util.*;

public class ListSnapshots {
    public static void main(String[] args) {
        List<String> source = new ArrayList<>(Arrays.asList("Java"));
        List<String> view = Collections.unmodifiableList(source);
        List<String> snapshot = List.copyOf(source);
        source.add("Python");
        System.out.println("view=" + view);
        System.out.println("snapshot=" + snapshot);
        try {
            snapshot.add("AI");
            throw new AssertionError("Mutation unexpectedly succeeded");
        } catch (UnsupportedOperationException expected) {
            System.out.println("snapshot rejects add");
        }
    }
}

2. Run the example

javac ListSnapshots.java
java ListSnapshots

Expected result

The view prints [Java, Python], the snapshot prints [Java], and the final line confirms the snapshot rejects add.

How it works

Collections.unmodifiableList wraps the original list. A caller cannot change it through the wrapper, but changes made through the original reference remain visible.

List.copyOf produces an unmodifiable list containing the source elements at the time of the call. Later structural changes to the mutable source do not appear in that result.

Both approaches are shallow. If the list holds mutable objects, changing an object can still affect what you observe through either list. An immutable data model requires decisions about the elements as well as the collection.

copyOf rejects null elements. Check your data before adopting it in an existing code path. For configuration snapshots or return values you want to detach from a mutable source, this distinction is often more useful than simply asking whether add is allowed.

Test the edge cases

  • Change the source before copying and compare the result.
  • Try a null element and observe copyOf rejecting it.
  • Replace strings with mutable objects to explore shallow copying.

Keep learning

Browse related tutorials. Reference: official documentation.

Reviewed 7 November 2026. AI-dependent examples require your own local model; generated text varies.