0Pricing
Java Academy · Урок

EnumSet и EnumMap

Используйте EnumSet и EnumMap для эффективных типобезопасных коллекций на основе перечислений.

«EnumSet и EnumMap» — бесплатный урок Java Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

EnumSet и EnumMap

EnumSet и EnumMap — это высокооптимизированные реализации коллекций для перечислений. Внутри они используют битовые векторы, поэтому работают намного быстрее HashSet и HashMap при использовании ключей-перечислений.

EnumSet: создание наборов

EnumSet предоставляет методы-фабрики для создания наборов констант перечисления.

import java.util.*;

enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

EnumSet<Day> weekdays  = EnumSet.range(Day.MON, Day.FRI);
EnumSet<Day> weekend   = EnumSet.of(Day.SAT, Day.SUN);
EnumSet<Day> allDays   = EnumSet.allOf(Day.class);
EnumSet<Day> noDays    = EnumSet.noneOf(Day.class);

System.out.println(weekdays); // [MON, TUE, WED, THU, FRI]
System.out.println(weekend);  // [SAT, SUN]
System.out.println(allDays.size()); // 7

EnumSet: операции над наборами

EnumSet поддерживает стандартные операции над множествами: объединение, пересечение и дополнение. Благодаря реализации на основе битового вектора они выполняются чрезвычайно быстро.

import java.util.*;

enum Permission { READ, WRITE, DELETE, ADMIN, EXPORT }

EnumSet<Permission> userPerms  = EnumSet.of(Permission.READ, Permission.WRITE);
EnumSet<Permission> adminPerms = EnumSet.allOf(Permission.class);

// Complement
EnumSet<Permission> missing = EnumSet.complementOf(userPerms);
System.out.println("Missing: " + missing);
// Missing: [DELETE, ADMIN, EXPORT]

// Intersection (retain)
EnumSet<Permission> shared = EnumSet.copyOf(userPerms);
shared.retainAll(adminPerms);
System.out.println("Shared: " + shared); // [READ, WRITE]

EnumMap: сопоставление перечислений со значениями

EnumMap использует порядковый номер перечисления как индекс массива — поиск за O(1) быстрее, чем в HashMap.

import java.util.*;

enum Quarter { Q1, Q2, Q3, Q4 }

EnumMap<Quarter, Double> revenue = new EnumMap<>(Quarter.class);
revenue.put(Quarter.Q1, 125_000.0);
revenue.put(Quarter.Q2, 148_500.0);
revenue.put(Quarter.Q3, 132_750.0);
revenue.put(Quarter.Q4, 175_200.0);

double total = revenue.values().stream()
    .mapToDouble(Double::doubleValue).sum();
System.out.printf("Annual revenue: $%.0f%n", total);
// Annual revenue: $581450

EnumMap: перебор в порядке объявления

EnumMap всегда перебирает ключи в порядке объявления констант перечисления, а не в порядке добавления или хеширования.

import java.util.*;

enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }

EnumMap<Month, Integer> salesUnits = new EnumMap<>(Month.class);
salesUnits.put(Month.MAR, 1420);
salesUnits.put(Month.JAN, 980);
salesUnits.put(Month.FEB, 1105);

// Iterates JAN, FEB, MAR (declaration order, not insertion order)
for (var entry : salesUnits.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}
// JAN: 980
// FEB: 1105
// MAR: 1420

Система разрешений с EnumSet

Система разрешений на основе ролей, использующая EnumSet для эффективной проверки разрешений и понятного проектирования API.

import java.util.*;

enum Permission { READ, WRITE, DELETE, PUBLISH, ADMIN }

record Role(String name, EnumSet<Permission> permissions) {
    boolean can(Permission p) { return permissions.contains(p); }
}

Role editor = new Role("Editor",
    EnumSet.of(Permission.READ, Permission.WRITE, Permission.PUBLISH));
Role viewer = new Role("Viewer",
    EnumSet.of(Permission.READ));

System.out.println(editor.can(Permission.WRITE));  // true
System.out.println(viewer.can(Permission.DELETE)); // false

Флаги функций с EnumMap

EnumMap идеально подходит для систем флагов функций: быстрый поиск за O(1) по ключам-перечислениям.

import java.util.*;

enum Feature { DARK_MODE, BETA_EDITOR, AI_SUGGESTIONS, EXPORT_PDF }

class FeatureFlags {
    private final EnumMap<Feature, Boolean> flags = new EnumMap<>(Feature.class);

    public FeatureFlags() {
        for (Feature f : Feature.values()) flags.put(f, false);
    }

    public void enable(Feature f)  { flags.put(f, true); }
    public void disable(Feature f) { flags.put(f, false); }
    public boolean isEnabled(Feature f) { return flags.getOrDefault(f, false); }
}

FeatureFlags ff = new FeatureFlags();
ff.enable(Feature.DARK_MODE);
System.out.println(ff.isEnabled(Feature.DARK_MODE));      // true
System.out.println(ff.isEnabled(Feature.AI_SUGGESTIONS)); // false

Производительность: EnumSet и HashSet

EnumSet использует одну битовую маску типа long (для перечислений с не более чем 64 константами), поэтому операции выполняются за O(1) с помощью битовых операций. HashSet использует хеш-таблицы, что медленнее и требует больше дополнительных ресурсов.

import java.util.*;

enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

// EnumSet: bit operations on a long value
EnumSet<Day> workDays = EnumSet.range(Day.MON, Day.FRI);

// HashSet: hash table with boxing overhead
Set<Day> hashWorkDays = new HashSet<>(Arrays.asList(
    Day.MON, Day.TUE, Day.WED, Day.THU, Day.FRI));

// Both work, but EnumSet is ~5x faster for contains/add/remove
// and uses less memory
System.out.println(workDays.contains(Day.SAT));     // false
System.out.println(workDays.contains(Day.MON));     // true

getOrDefault и computeIfAbsent в EnumMap

EnumMap поддерживает все операции отображений, включая getOrDefault, computeIfAbsent и объединение значений.

import java.util.*;

enum Category { FOOD, ELECTRONICS, CLOTHING, BOOKS }

EnumMap<Category, List<String>> catalog = new EnumMap<>(Category.class);

// computeIfAbsent creates the list on first use
catalog.computeIfAbsent(Category.BOOKS, k -> new ArrayList<>()).add("Clean Code");
catalog.computeIfAbsent(Category.BOOKS, k -> new ArrayList<>()).add("Effective Java");
catalog.computeIfAbsent(Category.ELECTRONICS, k -> new ArrayList<>()).add("Laptop");

System.out.println(catalog.get(Category.BOOKS));
// [Clean Code, Effective Java]
System.out.println(catalog.getOrDefault(Category.FOOD, List.of()));
// []

Шаблон мультиизображения с EnumMap

Объедините EnumMap со значениями List, чтобы создать мультиизображение с ключами-перечислениями и эффективно группировать элементы по категориям.

import java.util.*;
import java.util.stream.*;

enum Priority { LOW, NORMAL, HIGH, URGENT }
record Task(String name, Priority priority) {}

List<Task> tasks = List.of(
    new Task("Fix login bug", Priority.URGENT),
    new Task("Update docs",   Priority.LOW),
    new Task("Add unit tests",Priority.NORMAL),
    new Task("Deploy hotfix", Priority.URGENT)
);

// Group by priority into EnumMap
EnumMap<Priority, List<Task>> byPriority = tasks.stream()
    .collect(Collectors.groupingBy(Task::priority,
        () -> new EnumMap<>(Priority.class),
        Collectors.toList()));

byPriority.forEach((p, ts) ->
    System.out.println(p + ": " + ts.stream().map(Task::name).collect(Collectors.joining(", "))));

Когда использовать EnumSet и EnumMap

Кратко о выборе:

  • EnumSet: когда нужен набор значений перечисления — разрешения, активные функции или выбранные дни
  • EnumMap: когда каждой константе перечисления сопоставляется значение — конфигурация для каждой функции, оценка для каждого уровня или элементы для каждой категории
  • Оба варианта: для ключей-перечислений предпочтительнее HashSet и HashMap — они быстрее, требуют меньше памяти и сохраняют порядок

Быстрая проверка

Какая внутренняя структура данных используется EnumSet для перечислений с 64 или менее константами?

Итоги: EnumSet и EnumMap

Главное:

  • EnumSet использует битовый вектор и чрезвычайно эффективен для небольших наборов перечислений
  • EnumSet.of(), range(), allOf(), noneOf() — распространённые методы-фабрики
  • EnumMap использует массивы с индексами по порядковым номерам — поиск за O(1), порядок соответствует объявлению
  • Используйте EnumSet для наборов разрешений, флагов функций и фильтрации дней недели
  • Используйте EnumMap для конфигурации по категориям, таблиц оценок и сопоставления функций со значениями
  • Оба типа сохраняют порядок объявления перечисления и эффективнее коллекций на основе хеширования

Часто задаваемые вопросы

Урок «EnumSet и EnumMap» бесплатный?

Да — полный текст урока «EnumSet и EnumMap» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «EnumSet и EnumMap»?

Используйте EnumSet и EnumMap для эффективных типобезопасных коллекций на основе перечислений. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «EnumSet и EnumMap»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Определение и использование перечислений
  2. Перечисления с полями и методами
  3. Выражения switch с перечислениями
  4. EnumSet и EnumMap
← Назад к Java Academy