JavaRush /Курсы /JAVA 25 SELF /Map.copyOf, Set.copyOf и другие утилиты

Map.copyOf, Set.copyOf и другие утилиты

JAVA 25 SELF
34 уровень , 1 лекция
Открыта

1. Методы copyOf: List.copyOf, Set.copyOf, Map.copyOf

Представьте ситуацию: есть метод, который должен вернуть список, множество или карту, но при этом важно, чтобы вызывающий код никак не смог изменить содержимое этой коллекции. Например, «список поддерживаемых валют» или «набор ролей пользователя». Любая случайная вставка/удаление способна нарушить бизнес-логику.

До Java 10 приходилось либо возвращать копию коллекции вручную, либо использовать обёртки вроде Collections.unmodifiableList(list). Обёртка защищает только от изменений через себя, но не от модификаций исходной коллекции.

Теперь у нас есть более надёжный способ — методы copyOf.

Что это такое?

Методы List.copyOf(Collection), Set.copyOf(Collection), Map.copyOf(Map) (Java 10) создают настоящую неизменяемую копию переданной коллекции или карты.

  • Копия не связана с исходной коллекцией: изменения оригинала не влияют на результат copyOf.
  • Изменить копию невозможно: любая модификация приведёт к UnsupportedOperationException.

Пример создания неизменяемой копии

import java.util.*;

public class CopyOfDemo {
    public static void main(String[] args) {
        List<String> modifiable = new ArrayList<>();
        modifiable.add("Java");
        modifiable.add("Python");

        // Создаём неизменяемую копию
        List<String> immutable = List.copyOf(modifiable);

        // Попробуем изменить копию
        try {
            immutable.add("C++"); // Бросит UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("Нельзя добавить элемент в неизменяемую коллекцию!");
        }

        // Изменяем исходный список
        modifiable.add("Kotlin");

        // Копия осталась неизменной!
        System.out.println("Исходный список: " + modifiable);
        System.out.println("Неизменяемая копия: " + immutable);
    }
}

Вывод программы:

Нельзя добавить элемент в неизменяемую коллекцию!
Исходный список: [Java, Python, Kotlin]
Неизменяемая копия: [Java, Python]

Кратко о каждом методе

  • List.copyOf(Collection) — возвращает неизменяемый список с элементами из исходной коллекции.
  • Set.copyOf(Collection) — возвращает неизменяемое множество с элементами из коллекции; дубликаты будут устранены.
  • Map.copyOf(Map) — возвращает неизменяемую карту с парами из исходной карты.

Пример с Set и Map

import java.util.*;

public class CopyOfSetMapDemo {
    public static void main(String[] args) {
        Set<String> modifiableSet = new HashSet<>(Set.of("A", "B", "C"));
        Set<String> immutableSet = Set.copyOf(modifiableSet);

        // Попробуем добавить элемент
        try {
            immutableSet.add("D");
        } catch (UnsupportedOperationException e) {
            System.out.println("Нельзя изменить Set!");
        }

        Map<String, Integer> modifiableMap = new HashMap<>();
        modifiableMap.put("Alice", 30);
        modifiableMap.put("Bob", 25);

        Map<String, Integer> immutableMap = Map.copyOf(modifiableMap);

        try {
            immutableMap.put("Charlie", 28);
        } catch (UnsupportedOperationException e) {
            System.out.println("Нельзя изменить Map!");
        }
    }
}

2. Особенности и ограничения методов copyOf

Null-элементы не допускаются

Как и фабричные методы List.of, Set.of, Map.of, методы copyOf запрещают null как элемент, ключ или значение. Если в исходной коллекции/карте есть null, при копировании будет брошен NullPointerException.

List<String> listWithNull = Arrays.asList("A", null, "B");
List<String> immutable = List.copyOf(listWithNull); // Бросит NullPointerException!

Коллекция действительно неизменяема

Любая попытка add/remove/put/replace приводит к UnsupportedOperationException.

Оригинал и копия не связаны

Изменения исходной коллекции не влияют на копию, и наоборот.

Если коллекция уже неизменяема — возвращается она же

Передали уже неизменяемую коллекцию (например, результат List.of(...)) — copyOf вернёт тот же объект (экономия памяти).

List<String> immutable = List.of("X", "Y");
List<String> copy = List.copyOf(immutable);

System.out.println(immutable == copy); // true

Конкретная реализация не гарантируется

Тип возвращаемой коллекции — просто List, Set или Map. Это не обязательно ArrayList, HashSet или HashMap. Не полагайтесь на детали реализации (например, внутренний тип и оптимизации); контракт — неизменяемость и соответствующий интерфейс.

3. Отличие copyOf от обёрток (unmodifiable wrappers)

Collections.unmodifiableList(list) создаёт обёртку над существующей коллекцией. Если исходная коллекция изменяется, эти изменения видны и в обёртке.

List.copyOf(list) создаёт новую неизменяемую коллекцию, не связанную с оригиналом.

Демонстрация разницы

import java.util.*;

public class WrapperVsCopyOf {
    public static void main(String[] args) {
        List<String> original = new ArrayList<>(List.of("A", "B"));

        // Обёртка
        List<String> wrapper = Collections.unmodifiableList(original);

        // Копия
        List<String> copy = List.copyOf(original);

        // Изменим оригинал
        original.add("C");

        System.out.println("Обёртка: " + wrapper); // [A, B, C]
        System.out.println("Копия: " + copy);      // [A, B]
    }
}

Вывод:

Обёртка: [A, B, C]
Копия: [A, B]

Обёртка отражает изменения оригинала, а копия остаётся неизменной.

4. Практические сценарии применения copyOf

Защита данных при возврате из метода

public class CurrencyService {
    private final List<String> currencies = new ArrayList<>(List.of("USD", "EUR", "JPY"));

    public List<String> getSupportedCurrencies() {
        // Никто не сможет изменить результат
        return List.copyOf(currencies);
    }
}

Теперь вызывающий код не сможет добавить или удалить валюту из возвращаемого списка.

Передача между слоями приложения

Когда данные передаются между слоями (DAO → сервис → контроллер), используйте copyOf, чтобы быть уверенным: никто не изменит коллекцию «по дороге».

Безопасная публикация в многопоточных программах

Неизменяемые коллекции — удобный способ безопасно «расшарить» данные между потоками без дополнительной синхронизации.

5. Типичные ошибки при использовании copyOf и неизменяемых коллекций

Ошибка № 1: попытка вставить null. Методы copyOf (как и of) не терпят null — ни как элемент списка/набора, ни как ключ/значение карты. При копировании получите NullPointerException.

Ошибка № 2: путаница между обёрткой и копией. copyOf создаёт независимую копию: изменения оригинала не видны в копии. А вот Collections.unmodifiableList(list) лишь заворачивает исходную коллекцию: все изменения оригинала будут отражаться в обёртке.

Ошибка № 3: попытка изменить неизменяемую коллекцию. Вызовы add/remove/put на коллекциях из copyOf/of приводят к UnsupportedOperationException.

Ошибка № 4: забыли про ограничения фабрик карт. Например, Map.of() поддерживает до 10 пар ключ–значение. Если нужно больше — используйте Map.ofEntries(...) или соберите изменяемую карту и затем примените Map.copyOf.

Ошибка № 5: полагаться на конкретную реализацию. Не ожидайте, что copyOf вернёт именно ArrayList или HashSet. Гарантируются только интерфейс (List/Set/Map) и неизменяемость.

1
Задача
JAVA 25 SELF, 34 уровень, 1 лекция
Недоступна
Корзина магазина: живое представление против снимка 🛒
Корзина магазина: живое представление против снимка 🛒
1
Задача
JAVA 25 SELF, 34 уровень, 1 лекция
Недоступна
Конфигурация игры: фиксируем настройки релиза 🎮
Конфигурация игры: фиксируем настройки релиза 🎮
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ