EnumMap
Эффективные карты с ключами-перечислениями
«EnumMap» — бесплатный урок Java Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Что такое EnumMap
EnumMap — специализированная реализация Map, ключами которой являются константы одного типа перечисления.
Внутри он основан на обычном массиве, индексируемом по ordinal константы. Благодаря этому поиск и вставка выполняются чрезвычайно быстро, без хеширования.
import java.util.EnumMap;
import java.util.Map;
public class Main {
enum Day { MON, TUE, WED }
public static void main(String[] args) {
Map<Day, String> plan = new EnumMap<>(Day.class);
plan.put(Day.MON, "Gym");
plan.put(Day.WED, "Swim");
System.out.println(plan);
}
}Для создания нужен класс
EnumMap должен заранее знать тип ключа, поэтому конструктор принимает объект Class: new EnumMap<>(Day.class).
Так он определяет размер внутреннего массива по количеству констант.
import java.util.EnumMap;
public class Main {
enum Priority { LOW, MEDIUM, HIGH }
public static void main(String[] args) {
EnumMap<Priority, Integer> counts = new EnumMap<>(Priority.class);
counts.put(Priority.HIGH, 5);
System.out.println(counts.get(Priority.HIGH));
}
}Скорость благодаря массиву
Поскольку ключи хранятся в array[ordinal], get и put напрямую обращаются к элементам массива и работают быстрее, чем хеширование и обход корзины в HashMap.
Ключ не упаковывается, и вызов hashCode не выполняется.
import java.util.EnumMap;
public class Main {
enum Slot { A, B, C, D }
public static void main(String[] args) {
EnumMap<Slot, Integer> m = new EnumMap<>(Slot.class);
for (Slot s : Slot.values()) m.put(s, s.ordinal() * 10);
System.out.println(m.get(Slot.C));
}
}Перебор в порядке ключей
EnumMap выполняет перебор записей в естественном порядке ключей, то есть в порядке их объявления.
В отличие от HashMap это обеспечивает стабильный и предсказуемый вывод.
import java.util.EnumMap;
import java.util.Map;
public class Main {
enum Phase { PLAN, BUILD, SHIP }
public static void main(String[] args) {
Map<Phase, Integer> hours = new EnumMap<>(Phase.class);
hours.put(Phase.SHIP, 3);
hours.put(Phase.PLAN, 8);
hours.put(Phase.BUILD, 20);
for (Map.Entry<Phase, Integer> e : hours.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue());
}
}
}Пустые ключи запрещены
EnumMap не допускает пустые ключи; пустой ключ вызывает NullPointerException.
Пустые значения разрешены, как и в других картах.
import java.util.EnumMap;
public class Main {
enum K { A, B }
public static void main(String[] args) {
EnumMap<K, String> m = new EnumMap<>(K.class);
m.put(K.A, null); // null value is fine
System.out.println("A -> " + m.get(K.A));
System.out.println("contains A: " + m.containsKey(K.A));
}
}Группировка с помощью EnumMap
Распространённый вариант применения — подсчёт или группировка по категории перечисления. merge делает подсчёт лаконичным.
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
public class Main {
enum Type { FRUIT, VEG, MEAT }
record Item(String name, Type type) {}
public static void main(String[] args) {
List<Item> items = List.of(new Item("apple", Type.FRUIT), new Item("pear", Type.FRUIT), new Item("beef", Type.MEAT));
Map<Type, Integer> counts = new EnumMap<>(Type.class);
for (Item i : items) counts.merge(i.type(), 1, Integer::sum);
System.out.println(counts);
}
}getOrDefault и computeIfAbsent
Работают все стандартные методы Map, предоставленные по умолчанию. getOrDefault избавляет от проверок на пустое значение, а computeIfAbsent лениво создаёт значения.
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
public class Main {
enum Group { A, B }
public static void main(String[] args) {
Map<Group, List<String>> m = new EnumMap<>(Group.class);
m.computeIfAbsent(Group.A, k -> new ArrayList<>()).add("first");
System.out.println(m);
System.out.println(m.getOrDefault(Group.B, List.of()));
}
}Конструктор копирования
У EnumMap есть конструктор копирования, принимающий другую Map. Если исходная карта является EnumMap, тип ключа выводится из неё.
import java.util.EnumMap;
import java.util.Map;
public class Main {
enum K { X, Y }
public static void main(String[] args) {
EnumMap<K, Integer> a = new EnumMap<>(K.class);
a.put(K.X, 1);
EnumMap<K, Integer> b = new EnumMap<>(a);
b.put(K.Y, 2);
System.out.println("copy: " + b);
}
}EnumMap и массивы с индексами ordinal
До появления EnumMap использовались обычные массивы с индексами ordinal(). Это хрупкий и небезопасный подход.
EnumMap обеспечивает ту же скорость работы с массивом, а также типобезопасность, безопасность границ и понятный API Map.
import java.util.EnumMap;
import java.util.Map;
public class Main {
enum Season { SPRING, SUMMER, FALL, WINTER }
public static void main(String[] args) {
Map<Season, String> mood = new EnumMap<>(Season.class);
mood.put(Season.SUMMER, "sunny");
// Safer and clearer than String[] indexed by ordinal
System.out.println(mood.get(Season.SUMMER));
}
}Вложенные EnumMap
Можно вкладывать EnumMap друг в друга, чтобы представить матрицу с ключами из двух перечислений, например таблицу переходов состояний.
import java.util.EnumMap;
import java.util.Map;
public class Main {
enum State { SOLID, LIQUID, GAS }
enum Transition { MELT, FREEZE }
public static void main(String[] args) {
Map<State, Map<Transition, State>> table = new EnumMap<>(State.class);
Map<Transition, State> solid = new EnumMap<>(Transition.class);
solid.put(Transition.MELT, State.LIQUID);
table.put(State.SOLID, solid);
System.out.println(table.get(State.SOLID).get(Transition.MELT));
}
}Выбор подходящего типа
Практическое правило:
- Если ключи являются константами перечисления и Вам нужна карта, используйте EnumMap.
- Если Вам нужно множество перечислений, используйте EnumSet.
- Если ключами могут быть произвольные объекты, используйте HashMap.
import java.util.EnumMap;
import java.util.Map;
public class Main {
enum Level { DEBUG, INFO, WARN, ERROR }
public static void main(String[] args) {
Map<Level, Integer> thresholds = new EnumMap<>(Level.class);
for (Level l : Level.values()) thresholds.put(l, l.ordinal() * 100);
System.out.println(thresholds);
}
}Быстрая проверка
Проверьте свои знания об EnumMap.
Итоги
Вы изучили EnumMap:
- Map на основе массива, ключами которого являются константы перечисления.
- Конструктор принимает ключевой Class; пустые ключи запрещены.
- Перебор выполняется в порядке объявления.
- Это безопасная и быстрая замена массивам с индексами ordinal.
Вы завершили продвинутый курс по перечислениям.
import java.util.EnumMap;
public class Main {
enum K { A }
public static void main(String[] args) {
EnumMap<K, String> m = new EnumMap<>(K.class);
m.put(K.A, "done");
System.out.println("EnumMap course complete: " + m.get(K.A));
}
}Часто задаваемые вопросы
Урок «EnumMap» бесплатный?
Да — полный текст урока «EnumMap» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «EnumMap»?
Эффективные карты с ключами-перечислениями Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «EnumMap»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.