Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Collections.unmodifiableList is a live view, not a snapshot

Last updated: 1 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

Collections.unmodifiableList rejects mutation through the wrapper but reflects changes made through its backing list.

Block one path, not every alias

The wrapper's add and remove methods throw UnsupportedOperationException. A code path that still owns the mutable source can append, and the wrapper then exposes the appended element. This is read-only access through one reference, not immutability of the underlying state.

If an API must freeze the container membership at handoff time, List.copyOf takes an unmodifiable container snapshot. It still retains references to mutable elements. Stronger isolation requires a policy for those elements too.

Protect traversal when sharing across threads

An unmodifiable view does not synchronize its source. A writer on the backing LinkedList can invalidate a reader's traversal. Synchronized wrappers require a specific locking discipline, and a private snapshot can avoid shared mutation when its copying cost is acceptable.

The wrapper also retains the backing list. Holding the wrapper for a long time keeps that backing collection and its elements reachable.

Working program

Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class LiveDispatchReadOnlyView {
    public static void main(String[] args) {
        List<String> mutable = new ArrayList<>();
        mutable.add("load-15");
        List<String> readOnly = Collections.unmodifiableList(mutable);
        mutable.add("seal-47");
        System.out.println(readOnly);
        try { readOnly.add("ship-82"); }
        catch (UnsupportedOperationException rejected) { System.out.println("wrapper edit rejected"); }
        System.out.println(mutable.size());
    }
}

Output

Output
[load-15, seal-47]
wrapper edit rejected
2

Cost and ownership

Wrapping does not copy n elements. Reads delegate to the backing list, so their cost follows its implementation. The wrapper and source share one mutable container and all referenced element objects.

Common Mistakes

  • Do not describe an unmodifiable view as a frozen snapshot.
  • Do not keep a mutable backing alias if the consumer requires stable membership.
  • Do not infer thread safety from the word unmodifiable.

Read next

list copyof element alias, Arrays.asList in Java: fixed-size list backed by the original array, Java synchronizedList iteration: hold the wrapper lock, Java collection factories: rejected updates and shallow element ownership.

java
collections
collections-unmodifiable-live-view
Storage details