Java ProgramsCollectionsCollections Unmodifiable Example

Collections Unmodifiable Example in Java

intermediate·  Collections  ·  Immutability

Problem

Collections.unmodifiableList() returns a read-only VIEW of an existing mutable list, not an independent immutable copy — the wrapper blocks direct mutation, but changes made through the original list still show up in the view.

Wrap a mutable List so it can't be modified directly, and confirm changes to the original list still appear through the wrapper.

Input
mutable = [Apple, Banana]; view = unmodifiableList(mutable); mutable.add("Cherry")
Output
View after mutating original: [Apple, Banana, Cherry]

Java Program

Java
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class CollectionsUnmodifiableDemo { public static void main(String[] args) { List<String> mutable = new ArrayList<>(List.of("Apple", "Banana")); List<String> view = Collections.unmodifiableList(mutable); // wraps mutable, doesn't copy it mutable.add("Cherry"); // changing the original still shows up through the view System.out.println("View after mutating original: " + view); try { view.add("Date"); } catch (UnsupportedOperationException e) { System.out.println("view.add() blocked: UnsupportedOperationException"); } } }

Output

View after mutating original: [Apple, Banana, Cherry] view.add() blocked: UnsupportedOperationException

Core Logic

Wrapping the original list, instead of copying it, means the wrapper has no data of its own — it always reflects whatever the original list currently contains.

How It Works
  1. 1Collections.unmodifiableList(mutable) doesn't copy mutable's elements — it returns a thin wrapper that delegates every read straight through to mutable.
  2. 2Calling view.add(...) directly throws UnsupportedOperationException, since the wrapper's mutating methods are deliberately disabled.
  3. 3But calling mutable.add("Cherry") on the ORIGINAL list still works fine — mutable itself was never made immutable, only the view reference blocks mutation.
  4. 4Reading view again after that shows "Cherry" included, since view was never a separate snapshot — it's still looking at the same underlying list.
After mutable.add("Cherry"), printing view shows [Apple, Banana, Cherry], even though nothing was ever added to view directly.
💡

Key Point: This is the key difference from List.of(): List.of() has no mutable original anywhere to leak changes from, while Collections.unmodifiableList() only blocks mutation through the wrapper itself — the backing list underneath is still fully mutable to anyone holding a reference to it.

Complexity
Time Complexity: O(1)Space Complexity: O(1)

Why: unmodifiableList() just wraps the existing list in a thin delegating object — it doesn't copy any of the n elements, so wrapping itself costs nothing proportional to the list's size.

Key Concepts

Collections.unmodifiableList()read-only viewbacking collection

Related Programs