Що таке 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. Коли потрібно робити захисну копію]]
- [[12. Як захистити колекцію від змін]]
- [[29. Як правильно працювати з колекціями в незмінних класах]]