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...
🟢 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
- Need true immutability — use
List.copyOf()(Java 10+) - Need protection from reflection — unmodifiableList is vulnerable
- 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
- unmodifiableList = view, not immutable
- List.copyOf = full isolation
- Objects inside can still be modified
- RandomAccess is preserved
- Defensive copy for security-critical code
- 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) throwsUnsupportedOperationException - The wrapper protects the list structure, but NOT the objects inside (mutable User can be modified)
unmodifiableListvsList.copyOf(): view (connection to original) vs full copy (isolation)RandomAccessmarker 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,
UnmodifiableRandomAccessListalsoimplements 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]]