Вопрос 13 · Раздел 13

Что такое Collections.unmodifiableList() и как это работает?

Collections.unmodifiableList() — это метод, который создаёт "только для чтения" обёртку — объект-посредник, который перенаправляет все вызовы к оригинальному списку, но блокируе...

Версии по языкам: English Russian Ukrainian

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. Как правильно работать с коллекциями в иммутабельных классах]]