Question 23 · Section 4

What is Collections.unmodifiableList()?

The wrapper only protects the list structure (adding/removing elements), not the objects themselves. If deep immutability is needed — make User immutable (final fields, no sette...

Language versions: English Russian Ukrainian

🟢 Junior Level

unmodifiableList — a wrapper that prohibits modifying the list.

List<String> list = new ArrayList<>(List.of("A", "B"));
List<String> unmod = Collections.unmodifiableList(list);

unmod.get(0);       // ✅ OK → "A"
unmod.add("C");     // ❌ UnsupportedOperationException!

IMPORTANT: this is a view, not a copy!

list.add("C");      // Modified the original
unmod.get(2);       // → "C" (change is visible!)

🟡 Middle Level

View vs Immutable

// View (unmodifiableList):
List<String> view = Collections.unmodifiableList(original);
original.add("X");  // Change is visible in view!

// Immutable (List.of):
List<String> immutable = List.of("A", "B");
// → Cannot be modified BY ANYTHING
// → No connection to the original

unmodifiableCollection returns a Collection (no get(int), subList). unmodifiableList returns a List (preserves indexed access). Use unmodifiableList when the calling code needs List methods.

When to use

// ✅ Protecting internal state
public class UserService {
    private List<User> users = new ArrayList<>();

    public List<User> getUsers() {
        return Collections.unmodifiableList(users);
        // Client cannot modify the internal list!
    }
}

// ✅ Read-only access
public void printAll(List<String> list) {
    List<String> readOnly = Collections.unmodifiableList(list);
    // Guarantee: the method won't modify the list
}

Does not protect objects inside

List<User> users = Collections.unmodifiableList(list);
users.get(0).setName("Hacker");  // ✅ Possible!
// → The wrapper protects the list structure, not the objects!

The wrapper only protects the list structure (adding/removing elements), not the objects themselves. If deep immutability is needed — make User immutable (final fields, no setters) or use deep copy when wrapping.


🔴 Senior Level

When NOT to use unmodifiableList

  1. Need true immutability — use List.copyOf() (Java 10+)
  2. Need protection from reflection — unmodifiableList is vulnerable
  3. Collection will be modified from another thread — the original needs synchronization

RandomAccess preservation

// ArrayList implements RandomAccess
// UnmodifiableRandomAccessList also does!
// → binarySearch works optimally

// LinkedList is NOT RandomAccess
// UnmodifiableList also NOT RandomAccess

List.copyOf (Java 10+)

// Copy → no connection to the original
List<String> copy = List.copyOf(original);
original.add("X");  // Not visible in copy!

// unmodifiableList → view → connection exists
// List.copyOf → copy → no connection

Defensive Copying

// Service APIs:
// unmodifiableList → client can reach the original
// List.copyOf → full isolation (safer)

// But: List.copyOf = O(n) copying
// unmodifiableList = O(1) wrapper

Production Experience

Real scenario: original leak

// ❌ Client found the original via reflection
List<String> view = Collections.unmodifiableList(secret);
// → Found the `list` field inside the wrapper
// → Modified it!

// ✅ List.copyOf → no original
List<String> safe = List.copyOf(secret);

Best Practices

  1. unmodifiableList = view, not immutable
  2. List.copyOf = full isolation
  3. Objects inside can still be modified
  4. RandomAccess is preserved
  5. Defensive copy for security-critical code
  6. O(1) creation vs O(n) copying

Summary for Senior

  • View → connection to the original
  • List.copyOf → isolation
  • Does not protect objects inside
  • RandomAccess is preserved
  • O(1) wrapper creation
  • Defensive copy for security

🎯 Interview Cheat Sheet

Must know:

  • unmodifiableList() returns a view, not a copy — changes to the original are visible
  • Any modification (add, remove, set) throws UnsupportedOperationException
  • The wrapper protects the list structure, but NOT the objects inside (mutable User can be modified)
  • unmodifiableList vs List.copyOf(): view (connection to original) vs full copy (isolation)
  • RandomAccess marker is preserved — optimization for binary search
  • Wrapper creation is O(1), List.copyOf() copying is O(n)
  • For security-critical code — List.copyOf() or defensive copy

Frequent follow-up questions:

  • Are changes to the original visible through unmodifiableList? — Yes, it’s a view, not a copy.
  • Does unmodifiableList protect against modifying objects inside the list? — No, only against modifying the list structure (add/remove). The objects themselves need to be made immutable.
  • When to choose List.copyOf() over unmodifiableList()? — When full isolation from the original is needed (Java 10+).
  • Is RandomAccess preserved through the wrapper? — Yes, UnmodifiableRandomAccessList also implements RandomAccess.

Red flags (DO NOT say):

  • “unmodifiableList creates a copy of the data” — no, it’s a view on the original
  • “unmodifiableList is true immutability” — no, the original can still be modified
  • “Objects inside are also protected” — the wrapper only protects the structure, not the contents
  • “List.copyOf() is the same as unmodifiableList” — List.copyOf() creates a copy, with no connection to the original

Related topics:

  • [[24. How does Collections.unmodifiableList() work internally]]
  • [[22. How to get a synchronized collection]]
  • [[31. What operations does the Collection interface support]]