Питання 23 · Розділ 4

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

Обгортка захищає тільки структуру списку (додавання/видалення елементів), але не самі об'єкти. Якщо потрібна глибока незмінюваність — робіть User immutable (final поля, немає se...

Мовні версії: 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]]