Как посчитать элементы по значениям enum
Короткий ответ
Возьмите EnumMap и увеличивайте счётчик через merge(). Ключи выводятся в порядке объявления enum:
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}Через стрим
groupingBy() с фабрикой EnumMap и counting():
enum Status { NEW, PAID, SHIPPED }
record Order(int id, Status status) {}
void main() {
List<Order> orders = List.of(
new Order(1, Status.PAID), new Order(2, Status.NEW), new Order(3, Status.PAID));
Map<Status, Long> counts = orders.stream()
.collect(Collectors.groupingBy(Order::status,
() -> new EnumMap<>(Status.class), Collectors.counting()));
IO.println(counts);
}Результат:
{NEW=1, PAID=2}С нулями для всех значений
Значения, которых не было, в карту не попадают. Чтобы показать и их, сначала заполните карту нулями:
enum Status { NEW, PAID, SHIPPED }
void main() {
List<Status> orders = List.of(Status.PAID, Status.NEW, Status.PAID);
Map<Status, Integer> counts = new EnumMap<>(Status.class);
for (Status s : Status.values()) {
counts.put(s, 0);
}
for (Status s : orders) {
counts.merge(s, 1, Integer::sum);
}
counts.forEach((status, n) -> IO.println(status + ": " + n));
}Результат:
NEW: 1
PAID: 2
SHIPPED: 0Частая ошибка
Считать через HashMap: результат тот же, но порядок ключей зависит от хеш-кодов и может меняться от запуска к запуску. С EnumMap порядок всегда совпадает с порядком констант, а работает он ещё и быстрее. Второй вариант ошибки — считать по ordinal() в массиве int[]: код ломается, когда в enum добавляют новую константу в середину.
Функции из этого ответа
- EnumMap — Словарь, ключи которого — значения одного enum. Быстрый и хранит ключи в порядке объявления.
- Enum.values() — Методы каждого enum: values() — все константы, valueOf() — константа по имени, ordinal() и name() — номер и имя.
- Collectors.groupingBy() — Сборщик, который раскладывает элементы по группам с общим ключом.
- Map.merge() — Добавляет значение по ключу или объединяет его с уже существующим.