Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Коллекции для перечислений

Когда ключи словаря или элементы множества — константы перечисления (enum), вместо HashMap и HashSet лучше взять EnumMap и EnumSet. Они быстрее, занимают меньше памяти и хранят константы в порядке их объявления. Само перечисление, его методы values(), valueOf() и ordinal() разобраны в уроке о перечислениях.

EnumMap

EnumMap — словарь, ключами которого служат константы одного перечисления. В конструктор передают класс перечисления, например Day.class:

enum Day {
    MON, TUE, WED, THU, FRI, SAT, SUN
}
 
void main() {
    Map<Day, String> plans = new EnumMap<>(Day.class);
    plans.put(Day.FRI, "кино");
    plans.put(Day.MON, "бассейн");
    plans.put(Day.WED, "английский");
    IO.println(plans);
}

Результат:

{MON=бассейн, WED=английский, FRI=кино}

Ключи идут в порядке объявления констант, а не в порядке добавления. Внутри EnumMap — обычный массив, поэтому поиск по ключу очень быстрый.

EnumSet

EnumSet — множество констант. Его не создают через new, а получают фабричными методами: of() — перечисленные константы, range() — все от одной до другой, allOf() — все, noneOf() — пустое множество:

enum Day {
    MON, TUE, WED, THU, FRI, SAT, SUN
}
 
void main() {
    Set<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);
    Set<Day> workdays = EnumSet.range(Day.MON, Day.FRI);
    IO.println(weekend);
    IO.println(workdays);
    IO.println(workdays.contains(Day.SAT));
}

Результат:

[SAT, SUN]
[MON, TUE, WED, THU, FRI]
false

Внутри EnumSet хранит константы как биты одного числа, как маски из урока о побитовых операторах, только код при этом остаётся читаемым.

Дополнение множества

EnumSet.complementOf() возвращает все константы, которых нет в данном множестве. Так удобно описывать «все, кроме»:

enum Day {
    MON, TUE, WED, THU, FRI, SAT, SUN
}
 
void main() {
    EnumSet<Day> busy = EnumSet.of(Day.MON, Day.WED, Day.FRI);
    EnumSet<Day> free = EnumSet.complementOf(busy);
    IO.println(free);
}

Результат:

[TUE, THU, SAT, SUN]

Подсчёт по константам

EnumMap хорошо подходит для подсчёта: ключ — константа, значение — счётчик. Метод merge() прибавляет единицу или создаёт запись, если её ещё нет:

enum Status {
    NEW, PAID, SHIPPED
}
 
void main() {
    List<Status> orders = List.of(Status.PAID, Status.NEW, Status.PAID, Status.SHIPPED, Status.PAID);
    Map<Status, Integer> counts = new EnumMap<>(Status.class);
    for (Status s : orders) {
        counts.merge(s, 1, Integer::sum);
    }
    IO.println(counts);
}

Результат:

{NEW=1, PAID=3, SHIPPED=1}

Integer::sum — ссылка на метод, который складывает два числа: старое значение и единицу.

Поиск константы по строке

valueOf() падает, если такой константы нет. Когда строка приходит от пользователя, надёжнее перебрать values() и сравнить имена без учёта регистра:

enum Size {
    SMALL, MEDIUM, LARGE
}
 
Size findSize(String text) {
    for (Size s : Size.values()) {
        if (s.name().equalsIgnoreCase(text)) {
            return s;
        }
    }
    return Size.MEDIUM;
}
 
void main() {
    IO.println(findSize("large"));
    IO.println(findSize("XXL"));
}

Результат:

LARGE
MEDIUM

Частая ошибка

Номер ordinal() зависит от порядка констант в коде. Если сохранить его в файл или базу, а потом добавить новую константу в начало, старые номера начнут означать другое. Здесь 1 сохранили, когда перечисление было SMALL, MEDIUM, LARGE, а затем в начало добавили XS:

enum Size {
    XS, SMALL, MEDIUM, LARGE
}
 
void main() {
    int saved = 1;
    IO.println(Size.values()[saved]);
}

Результат:

SMALL

Вместо MEDIUM получился SMALL. Сохраняйте имя константы (name()) или собственное поле с кодом, а для данных по константам используйте EnumMap вместо массива с индексом ordinal().

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.