JavaRush /Курсы /JAVA 25 SELF /List.of, Set.of, Map.of — неизменяемые коллекции

List.of, Set.of, Map.of — неизменяемые коллекции

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

1. Введение

Классические коллекции: гибкость и ловушки

Когда вы создаёте коллекцию с помощью new ArrayList<>(), вы получаете структуру, которую можно свободно изменять: добавлять, удалять, менять элементы. Это удобно, когда вы строите данные «на лету». Но что, если вы передаёте эту коллекцию в другой класс или метод, где её не должны менять? А если вы случайно передадите эту коллекцию наружу, и кто-то её изменит? Вот тут и начинаются проблемы.

Пример классической ошибки

import java.util.*;

public class Example {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");

        // Передаём коллекцию "вовне"
        processNames(names);

        // Ожидаем, что список не изменился...
        System.out.println(names);
    }

    public static void processNames(List<String> list) {
        // А кто-то взял и удалил элемент!
        list.remove("Bob");
    }
}

Вывод:

[Alice, Charlie]

Ваша коллекция изменилась, хотя вы этого совершенно не планировали. В больших проектах такие «сюрпризы» легко превращаются в крайне неприятные и трудноуловимые баги.

Опасность в том, что код, работающий с коллекцией, может внезапно столкнуться с непредсказуемыми изменениями данных. К тому же всегда есть риск потери информации — кто-то случайно удалил элемент или переписал его. А если коллекцию одновременно меняют из разных потоков, можно нарваться не только на ConcurrentModificationException, но и на ещё более коварную проблему — неконсистентные данные.

Защита коллекций: старый подход

До Java 9 приходилось использовать методы-обёртки вроде Collections.unmodifiableList(...), о которых мы говорили на прошлом уровне. Они помогают вернуть «замороженную» коллекцию. Но этот способ не всегда удобен и не решает всех проблем (о нём подробнее — в следующей лекции).

2. Современное решение: фабричные методы List.of, Set.of, Map.of

В Java 9 появились новые статические методы в интерфейсах коллекций: List.of, Set.of, Map.of. Они позволяют быстро и удобно создать коллекцию, которую нельзя изменить. Это как если бы вы сделали коллекцию и сразу залили её в бетон — никто не сможет добавить, удалить или изменить элементы.

Пример создания неизменяемых коллекций

import java.util.*;

public class ImmutableDemo {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob", "Charlie");
        Set<Integer> numbers = Set.of(1, 2, 3);
        Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25, "Charlie", 28);

        System.out.println(names);
        System.out.println(numbers);
        System.out.println(ages);
    }
}

Вывод:

[Alice, Bob, Charlie]
[1, 2, 3]
{Alice=30, Bob=25, Charlie=28}

Как это работает?

  • List.of(...) — создаёт неизменяемый список.
  • Set.of(...) — создаёт неизменяемое множество.
  • Map.of(...) — создаёт неизменяемую карту (до 10 пар ключ-значение; для большего числа используйте Map.ofEntries(...)).

Внимание! Коллекции, созданные этими методами, не допускают изменений. Любая попытка добавить, удалить или заменить элемент приведёт к выбросу исключения.

3. Примеры использования и «подводные камни»

Пример: попытка изменить коллекцию

import java.util.*;

public class ImmutableFail {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob");
        // names.add("Charlie"); // Ошибка на этапе выполнения!
        try {
            names.add("Charlie");
        } catch (UnsupportedOperationException ex) {
            System.out.println("Нельзя добавить элемент: " + ex.getClass().getSimpleName());
        }
    }
}

Вывод:

Нельзя добавить элемент: UnsupportedOperationException

Пример: попытка добавить null

import java.util.*;

public class NullFail {
    public static void main(String[] args) {
        try {
            List<String> badList = List.of("Alice", null, "Bob");
        } catch (NullPointerException ex) {
            System.out.println("Null запрещён: " + ex.getClass().getSimpleName());
        }
    }
}

Вывод:

Null запрещён: NullPointerException

Пример: дубликаты в Set.of

import java.util.*;

public class DuplicatesFail {
    public static void main(String[] args) {
        try {
            Set<String> badSet = Set.of("one", "two", "one");
        } catch (IllegalArgumentException ex) {
            System.out.println("Дубликаты запрещены: " + ex.getClass().getSimpleName());
        }
    }
}

Вывод:

Дубликаты запрещены: IllegalArgumentException

Пример: Map.of с большим количеством пар

import java.util.*;

public class MapOfLarge {
    public static void main(String[] args) {
        // Map.of поддерживает до 10 пар ключ-значение
        Map<String, Integer> map = Map.of(
            "one", 1, "two", 2, "three", 3, "four", 4, "five", 5,
            "six", 6, "seven", 7, "eight", 8, "nine", 9, "ten", 10
        );
        System.out.println(map);

        // Для большего количества используйте Map.ofEntries
        Map<String, Integer> bigMap = Map.ofEntries(
            Map.entry("eleven", 11),
            Map.entry("twelve", 12),
            Map.entry("thirteen", 13)
            // ...и так далее
        );
        System.out.println(bigMap);
    }
}

4. Особенности и ограничения неизменяемых коллекций

Нельзя изменять.
Любая попытка добавить, удалить или изменить элемент приведёт к UnsupportedOperationException. Даже методы, которые обычно разрешены (add, remove, set), не работают.

Нельзя использовать null.
Если вы попытаетесь добавить null как элемент списка или множества, или как ключ/значение в карту, получите NullPointerException. Это сделано для безопасности: null-элементы часто приводят к ошибкам в коллекциях.

Не гарантируется конкретная реализация.
Вы не узнаете, какой именно класс лежит под капотом у коллекции, созданной через List.of и др. Не стоит делать instanceof ArrayList или пытаться привести коллекцию к какому-то конкретному типу.

Порядок элементов.
— У List.of порядок элементов сохраняется (как в обычном списке).
— У Set.of порядок не гарантируется (на практике может совпадать с порядком передачи аргументов, но лучше не полагаться).
— У Map.of порядок пар не гарантируется.

Быстродействие.
Коллекции, созданные через фабричные методы, обычно работают быстрее, чем обёртки над изменяемыми коллекциями, потому что не тратят память на лишние возможности.

5. Когда и зачем использовать неизменяемые коллекции

Константные наборы данных

Если у вас есть список, множество или карта, которые не должны меняться во время работы программы, используйте List.of, Set.of, Map.of. Например:

private static final List<String> ROLES = List.of("USER", "ADMIN", "MODERATOR");

Теперь никто не сможет добавить в этот список лишнюю роль.

Возвращение коллекций из методов

Если вы возвращаете коллекцию из метода и не хотите, чтобы её кто-то изменил снаружи:

public List<String> getDefaultNames() {
    return List.of("Alice", "Bob", "Charlie");
}

Получатель не сможет испортить ваши данные.

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

Когда вы передаёте коллекции между разными частями программы (например, между слоями Controller и Service в веб-приложении), лучше использовать неизменяемые коллекции, чтобы никто не мог «по-тихому» их изменить.

Безопасность и потокобезопасность

Неизменяемые коллекции по определению потокобезопасны в плане чтения: если их никто не может изменить, то их можно смело использовать из разных потоков без синхронизации.

6. Практические примеры для общего приложения

Допустим, в нашем учебном приложении есть список поддерживаемых команд:

public class Commands {
    public static final List<String> SUPPORTED_COMMANDS = List.of(
        "help", "exit", "list", "add", "remove"
    );
}

Если вы попытаетесь сделать так:

Commands.SUPPORTED_COMMANDS.add("hack_the_system");

Вы получите исключение и не сможете навредить приложению.

Или, например, если у вас есть карта с кодами ошибок:

public class ErrorCodes {
    public static final Map<Integer, String> CODES = Map.of(
        404, "Not Found",
        500, "Internal Server Error",
        403, "Forbidden"
    );
}

Любая попытка добавить новый код — выбросит исключение.

7. Сравнение подходов создания коллекций

Способ создания Можно менять? Можно null? Дубликаты? Потокобезопасность Пример
new ArrayList<>()
Да Да Да Нет
new ArrayList<>()
List.of(...)
Нет Нет Да Да*
List.of("a", "b")
Set.of(...)
Нет Нет Нет Да*
Set.of("a", "b")
Map.of(...)
Нет Нет Нет Да*
Map.of("a", 1, "b", 2)
Collections.unmodifiableList(...)
Нет Зависит от исходной Да Нет
Collections.unmodifiableList(list)

* — потокобезопасность только в плане неизменяемости: если коллекцию никто не меняет, её можно безопасно читать из разных потоков.

8. Типичные ошибки при работе с List.of, Set.of, Map.of

Ошибка №1: попытка изменить коллекцию.
Очень частая ошибка — попытаться добавить или удалить элемент из коллекции, созданной через List.of, Set.of или Map.of. Например, names.add("Dmitry") или ages.remove("Bob"). Это всегда приводит к UnsupportedOperationException на этапе выполнения.

Ошибка №2: попытка добавить null.
Если вы случайно передадите null в любой из методов (например, List.of("Alice", null)), получите NullPointerException. Неизменяемые коллекции Java 9+ не любят null — и это, на самом деле, хорошо.

Ошибка №3: дубликаты элементов в Set.of или Map.of.
Set.of("a", "b", "a") или Map.of("x", 1, "x", 2) приведут к IllegalArgumentException. Множество и карта по определению не могут содержать дубликаты.

Ошибка №4: ожидание конкретной реализации.
Не стоит делать так:

List<String> list = List.of("a", "b");
if (list instanceof ArrayList) { 
    // ...
} // Это всегда false!

Внутренняя реализация скрыта — не полагайтесь на детали реализации.

Ошибка №5: попытка использовать методы изменения коллекции.
Даже такие методы, как clear(), set(index, value) (у списка), будут выбрасывать исключения. Помните: коллекции, созданные через фабричные методы, неизменяемы.

1
Задача
JAVA 25 SELF, 34 уровень, 0 лекция
Недоступна
Строгая палитра цветов 🎨
Строгая палитра цветов 🎨
1
Задача
JAVA 25 SELF, 34 уровень, 0 лекция
Недоступна
Защита данных: никаких пустых записей и дубликатов! 🛡️
Защита данных: никаких пустых записей и дубликатов! 🛡️
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Andrey Уровень 1
27 сентября 2025
34