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

Что такое Collections.unmodifiableList()?

Обёртка защищает только структуру списка (добавление/удаление элементов), но не сами объекты. Если нужна глубокая неизменяемость — делайте User immutable (final поля, нет setter...

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

🟢 Junior Level

unmodifiableList — обёртка, которая запрещает изменять список.

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

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

ВАЖНО: это представление (view), не копия!

list.add("C");      // Изменили оригинал
unmod.get(2);       // → "C" (изменение видно!)

🟡 Middle Level

View vs Immutable

// View (unmodifiableList):
List<String> view = Collections.unmodifiableList(original);
original.add("X");  // Изменение видно в view!

// Immutable (List.of):
List<String> immutable = List.of("A", "B");
// → Нельзя изменить НИЧЕМ
// → Нет связи с оригиналом

unmodifiableCollection возвращает Collection (нет get(int), subList). unmodifiableList возвращает List (сохраняет индексный доступ). Используйте unmodifiableList, когда вызывающему коду нужны методы List.

Когда использовать

// ✅ Защита внутреннего состояния
public class UserService {
    private List<User> users = new ArrayList<>();
    
    public List<User> getUsers() {
        return Collections.unmodifiableList(users);
        // Клиент не сможет изменить внутренний список!
    }
}

// ✅ Read-only доступ
public void printAll(List<String> list) {
    List<String> readOnly = Collections.unmodifiableList(list);
    // Гарантия: метод не изменит список
}

Не защищает объекты внутри

List<User> users = Collections.unmodifiableList(list);
users.get(0).setName("Hacker");  // ✅ Можно!
// → Обёртка защищает структуру списка, не объекты!

Обёртка защищает только структуру списка (добавление/удаление элементов), но не сами объекты. Если нужна глубокая неизменяемость — делайте User immutable (final поля, нет setter’ов) или используйте deep copy при обёртывании.


🔴 Senior Level

Когда НЕ использовать unmodifiableList

  1. Нужна настоящая иммутабельность — используйте List.copyOf() (Java 10+)
  2. Нужна защита от рефлексии — unmodifiableList уязвим
  3. Коллекция будет меняться из другого потока — нужна синхронизация оригинала

RandomAccess preservation

// ArrayList implements RandomAccess
// UnmodifiableRandomAccessList тоже!
// → binarySearch работает оптимально

// LinkedList НЕ RandomAccess
// UnmodifiableList тоже НЕ RandomAccess

List.copyOf (Java 10+)

// Копия → нет связи с оригиналом
List<String> copy = List.copyOf(original);
original.add("X");  // Не видно в copy!

// unmodifiableList → view → связь есть
// List.copyOf → копия → связи нет

Defensive Copying

// API сервисов:
// unmodifiableList → клиент может добраться до оригинала
// List.copyOf → полная изоляция (безопаснее)

// Но: List.copyOf = O(n) копирование
// unmodifiableList = O(1) обёртка

Production Experience

Реальный сценарий: утечка оригинала

// ❌ Клиент нашёл оригинал через рефлексию
List<String> view = Collections.unmodifiableList(secret);
// → Нашёл поле list внутри обёртки
// → Изменил!

// ✅ List.copyOf → нет оригинала
List<String> safe = List.copyOf(secret);

Best Practices

  1. unmodifiableList = view, не immutable
  2. List.copyOf = полная изоляция
  3. Объекты внутри можно менять
  4. RandomAccess сохраняется
  5. Defensive copy для security-critical
  6. O(1) создание vs O(n) копирование

Резюме для Senior

  • View → связь с оригиналом
  • List.copyOf → изоляция
  • Не защищает объекты внутри
  • RandomAccess сохраняется
  • O(1) создание обёртки
  • Defensive copy для security

🎯 Шпаргалка для интервью

Обязательно знать:

  • unmodifiableList() возвращает view (представление), не копию — изменения оригинала видны
  • Любая модификация (add, remove, set) бросает UnsupportedOperationException
  • Обёртка защищает структуру списка, но НЕ объекты внутри (mutable User можно изменить)
  • unmodifiableList vs List.copyOf(): view (связь с оригиналом) vs полная копия (изоляция)
  • RandomAccess маркер сохраняется — оптимизация для бинарного поиска
  • Создание обёртки O(1), копирование List.copyOf() O(n)
  • Для security-critical кода — List.copyOf() или defensive copy

Частые уточняющие вопросы:

  • Видны ли изменения оригинала через unmodifiableList? — Да, это view, не копия.
  • Защищает ли unmodifiableList от изменения объектов внутри списка? — Нет, только от изменения структуры списка (add/remove). Сами объекты нужно делать immutable.
  • Когда выбрать List.copyOf() вместо unmodifiableList()? — Когда нужна полная изоляция от оригинала (Java 10+).
  • Сохраняется ли RandomAccess через обёртку? — Да, UnmodifiableRandomAccessList тоже implements RandomAccess.

Красные флаги (НЕ говорить):

  • «unmodifiableList создаёт копию данных» — нет, это view на оригинал
  • «unmodifiableList — это настоящая иммутабельность» — нет, оригинал можно изменить
  • «Объекты внутри тоже защищены» — обёртка защищает только структуру, не содержимое
  • «List.copyOf() — то же самое, что unmodifiableList» — List.copyOf() создаёт копию, без связи с оригиналом

Связанные темы:

  • [[24. Как работает Collections.unmodifiableList() внутри]]
  • [[22. Как получить synchronized коллекцию]]
  • [[31. Какие операции поддерживает интерфейс Collection]]