Что такое Collections.unmodifiableList() и как это работает?
Collections.unmodifiableList() — это метод, который создаёт "только для чтения" обёртку — объект-посредник, который перенаправляет все вызовы к оригинальному списку, но блокируе...
Junior Level
Collections.unmodifiableList() — это метод, который создаёт “только для чтения” обёртку — объект-посредник, который перенаправляет
все вызовы к оригинальному списку, но блокирует методы модификации.
List<String> list = new ArrayList<>();
list.add("A");
List<String> readOnly = Collections.unmodifiableList(list);
System.out.println(readOnly.get(0)); // "A" — можно читать
readOnly.add("B"); // ОШИБКА! UnsupportedOperationException
Важно: это обёртка, а не копия. Если изменить оригинальный список, изменения будут видны через обёртку.
Middle Level
Как это работает
Метод возвращает объект внутреннего класса Collections.UnmodifiableList, который:
- Чтение (
get(),size(),contains()) — делегирует оригинальному списку - Запись (
add(),remove(),clear()) — выбрасываетUnsupportedOperationException - Итератор — тоже обёрнутый,
remove()заблокирован
Главная ловушка
List<String> original = new ArrayList<>();
original.add("A");
List<String> readOnly = Collections.unmodifiableList(original);
original.add("B"); // Изменяем оригинал
System.out.println(readOnly); // [A, B] — изменения видны!
UnmodifiableList vs List.copyOf()
| Характеристика | unmodifiableList |
List.copyOf |
|---|---|---|
| Копирование | Нет | Да |
| Связь с оригиналом | Сохраняется | Разрывается |
| Допускает null | Да | Нет (NPE) |
| Скорость создания | O(1) | O(n) |
| Потокобезопасность | Нет | Нет |
// synchronizedList обеспечивает потокобезопасность, но НЕ иммутабельность. // unmodifiableList обеспечивает иммутабельность, но НЕ потокобезопасность.
Когда использовать
- В геттере —
unmodifiableListдля экономии памяти (если клиент не доберётся до оригинала) - В конструкторе —
List.copyOf()для настоящей защиты
Senior Level
Декоратор паттерн под капотом
UnmodifiableList — классический пример Decorator Pattern:
// Упрощённо
class UnmodifiableList<E> implements List<E> {
private final List<E> list; // ссылка на оригинал
public E get(int index) { return list.get(index); } // делегирование
public boolean add(E e) { throw new UnsupportedOperationException(); }
}
Производительность
- Создание: O(1) — никаких копий
- Чтение: O(1) — прямой вызов к оригиналу
- Память: минимальная — только обёртка
Thread-safety нюанс
unmodifiableList не делает список потокобезопасным. Если оригинальный список изменяется другим потоком, вы можете увидеть ConcurrentModificationException или неконсистентное состояние. Для потокобезопасности нужна CopyOnWriteArrayList или Collections.synchronizedList().
Предупреждение: доступ к оригиналу
Если вызывающий код может сохранить ссылку на оригинальный список, unmodifiableList не обеспечит защиту. caller может изменить оригинал напрямую!
Резюме для Senior
unmodifiableList— это View, а не копия- Эффективный способ (O(1)) ограничить права доступа к коллекции
- Не защищает от изменений оригинальной коллекции
- Для истинной иммутабельности предпочтительнее
List.of()иList.copyOf()
🎯 Шпаргалка для интервью
Обязательно знать:
unmodifiableList— обёртка (Decorator Pattern), не копия: O(1) создание, связь с оригиналом- Чтение делегирует оригиналу, запись выбрасывает
UnsupportedOperationException - Итератор тоже обёрнут —
remove()заблокирован - Главная ловушка: изменение оригинала видно через обёртку
unmodifiableListНЕ делает список потокобезопасным — нужнаCopyOnWriteArrayListилиsynchronizedList- Если caller сохранит ссылку на оригинал — unmodifiableList не обеспечит защиту
Частые уточняющие вопросы:
- UnmodifiableList vs List.copyOf? — UnmodifiableList — обёртка (O(1), связь), List.copyOf — копия (O(n), нет связи)
- Можно ли добавить элемент через обёртку? — Нет,
UnsupportedOperationException - UnmodifiableList потокобезопасен? — Нет, только блокирует запись; если оригинал меняется другим потоком — ConcurrentModificationException
- Когда использовать unmodifiableList? — В геттере, если клиент не доберётся до оригинала
Красные флаги (НЕ говорить):
- «UnmodifiableList — это копия» — это обёртка, оригинал виден
- «UnmodifiableList = thread-safe» — нет, нужна
synchronizedListилиCopyOnWriteArrayList synchronizedListобеспечивает иммутабельность» — нет, только потокобезопасность- «Итератор unmodifiableList позволяет remove()» — итератор тоже обёрнут, remove() заблокирован
Связанные темы:
- [[10. Что такое defensive copy (защитная копия)]]
- [[11. Когда нужно делать defensive copy]]
- [[12. Как защитить коллекцию от изменений]]
- [[29. Как правильно работать с коллекциями в иммутабельных классах]]