Як захистити колекцію від змін?
В 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. Коли потрібно робити захисну копію]]
- [[13. Що таке Collections.unmodifiableList() і як це працює]]
- [[14. В чому різниця між shallow copy та deep copy]]
- [[29. Як правильно працювати з колекціями в незмінних класах]]