0Pricing
Java Academy · Урок

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 — локальная установка не требуется.

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

  1. Перечисления с полями и методами
  2. Абстрактные методы в перечислениях
  3. EnumSet
  4. EnumMap
← Назад к Java Academy