How to count items by enum value
Short answer
Take an EnumMap and increase the counter with merge(). Keys are printed in enum declaration order:
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);
}Output:
{NEW=1, PAID=3, SHIPPED=1}With a stream
groupingBy() with an EnumMap factory and 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);
}Output:
{NEW=1, PAID=2}With zeros for every value
Values that never occurred do not appear in the map. To show them too, fill the map with zeros first:
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));
}Output:
NEW: 1
PAID: 2
SHIPPED: 0Common mistake
Counting with a HashMap: the result is the same, but the key order depends on hash codes and may change from run to run. With an EnumMap the order always matches the order of the constants, and it is faster too. Another form of the mistake is counting by ordinal() in an int[] array: the code breaks when a new constant is added in the middle of the enum.
Functions used in this recipe
- EnumMap — A map whose keys are values of one enum. Fast, and keeps keys in declaration order.
- Enum.values() — Methods of every enum: values() gives all constants, valueOf() finds one by name, ordinal() and name() give its index and name.
- Collectors.groupingBy() — A collector that sorts elements into groups that share a key.
- Map.merge() — Adds a value under a key or combines it with the existing one.