Как защитить коллекцию от изменений?
В Java есть несколько способов защитить коллекцию:
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)), не копия, связь с оригиналом, допускает nullList.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.ofvsList.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. Как правильно работать с коллекциями в иммутабельных классах]]