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

Как защитить коллекцию от изменений?

В Java есть несколько способов защитить коллекцию:

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

Junior Level

В Java есть несколько способов защитить коллекцию:

Способ 1: Collections.unmodifiableList()

List<String> original = new ArrayList<>();
original.add("A");
List<String> readOnly = Collections.unmodifiableList(original);

readOnly.add("B"); // Выбросит UnsupportedOperationException!

Но если изменить original, изменения будут видны через readOnly.

Способ 2: List.of() — Java 9+

List<String> immutable = List.of("A", "B", "C");
immutable.add("D"); // UnsupportedOperationException!

Способ 3: List.copyOf() — Java 10+

List<String> original = new ArrayList<>();
original.add("A");
List<String> immutable = List.copyOf(original); // создаст копию

Когда защита коллекции НЕ нужна

  • Private коллекции, которые никогда не покидают класс
  • Read-only справочники, загружаемые один раз при старте
  • Внутренние буферы, используемые только для вычислений

Middle Level

Сравнение подходов

Метод Делает копию? Связь с оригиналом Null элементы
Collections.unmodifiableList() Нет Живая связь Разрешены
new ArrayList(original) Да Нет связи Разрешены
List.of() Да Нет связи Запрещены
List.copyOf() Да Нет связи Запрещены

Пример “живой связи” unmodifiableList:

List<String> original = new ArrayList<>(List.of("A", "B"));
List<String> unmod = Collections.unmodifiableList(original);
original.add("C");
System.out.println(unmod); // [A, B, C] — unmodifiableList видит изменения original!

List.copyOf может вернуть тот же объект, если переданная коллекция уже иммутабельна. Это оптимизация JDK — лишняя копия не создаётся.

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

  • Возврат из геттера: List.copyOf() или Collections.unmodifiableList()
  • В конструкторе: List.copyOf() (создаёт независимую копию)
  • Константы: List.of() (оптимизированная иммутабельная коллекция)

Защита Map и Set

Map<String, Integer> immutableMap = Map.copyOf(originalMap);
Set<String> immutableSet = Set.copyOf(originalSet);

Важно: поверхностная защита

Все эти методы делают только Shallow защиту — если объекты в списке мутабельны, их состояние можно изменить:

List<User> users = List.copyOf(userList);
// users нельзя изменить, но:
users.get(0).setName("New Name"); // МОЖНО!

Senior Level

UnmodifiableList под капотом

Collections.unmodifiableList() использует паттерн Decorator. Внутренний класс UnmodifiableList:

  • Делегирует чтение оригиналу
  • Блокирует запись (UnsupportedOperationException)
  • Итератор тоже обёрнут — remove() заблокирован

Время создания: O(1), т.к. копия не создаётся.

List.copyOf под капотом

List.copyOf() создаёт новую коллекцию типа ImmutableCollections.ListN:

  • Оптимизирована по памяти (нет полей для расширения)
  • Если вход уже иммутабелен — вернёт его же (оптимизация)
  • Время создания: O(n)

Глубокая защита

Для полной иммутабельности мутабельных объектов в коллекции:

  • Используйте библиотеку Vavr (персистентные структуры)
  • Или ручное глубокое клонирование каждого элемента

Резюме для Senior

  • Для возврата данных — List.copyOf() или Collections.unmodifiableList()
  • Для констант — List.of() (Java 9+)
  • Все стандартные средства JDK — только Shallow Immutability
  • Для гарантированной неизменяемости всей структуры — Vavr или глубокое клонирование

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

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

  • 3 основных способа: Collections.unmodifiableList(), List.of(), List.copyOf()
  • unmodifiableList — обёртка (O(1)), не копия, связь с оригиналом, допускает null
  • List.of() — создаёт копию, нет связи, null запрещены, для констант
  • List.copyOf() — создаёт копию, нет связи, null запрещены, оптимизирован (может вернуть тот же объект)
  • Shallow защита: если объекты в списке мутабельны, их можно изменить через get(0).setName()
  • Для Map/Set: Map.copyOf(), Set.copyOf()

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

  • Что лучше — unmodifiableList или List.copyOf? — unmodifiableList для возврата из геттера (если оригинал недоступен), List.copyOf для конструктора
  • List.copyOf копирует если вход уже иммутабелен? — Нет, вернёт тот же объект (оптимизация JDK)
  • Как защитить мутабельные элементы в списке? — Deep Copy: клонировать каждый элемент
  • List.of vs List.copyOf? — List.of для литералов/констант, List.copyOf для копирования существующей коллекции

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

  • «UnmodifiableList — полноценная защита» — это обёртка, оригинал можно изменить
  • «List.copyOf допускает null» — выбросит NPE
  • «Если контейнер иммутабелен, элементы тоже» — нет, мутабельные элементы можно менять
  • unmodifiableList потокобезопасен» — нет, только иммутабельность, не thread-safety

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

  • [[10. Что такое defensive copy (защитная копия)]]
  • [[11. Когда нужно делать defensive copy]]
  • [[13. Что такое Collections.unmodifiableList() и как это работает]]
  • [[14. В чём разница между shallow copy и deep copy]]
  • [[29. Как правильно работать с коллекциями в иммутабельных классах]]