Мы снова рассмотрим другой специальный тип коллекции, который эффективно обрабатывает перечисления . На этот раз мы рассмотрим EnumMap s. Давайте рассмотрим некоторый код, который не использует EnumMap и вместо этого использует встроенную функцию порядковый номер . Как обсуждалось в предыдущей главе, это функция, которая должна использоваться только внутренними библиотеками, а не нашим кодом.
class PLant {
enum LifeCycle {ANNUAL, PERENNIAL, BIENNIAL }
final String name;
final LifeCycle lifeCycle;
Plant(String name, LifeCycle lifecycle) {
this.name = name;
this.lifecycle = lifecycle;
}
@Override
public String toString() {
return name;
}
}
Теперь предположим, что у нас в саду есть куча растений, и мы хотим собрать наши растения вместе в группы по их жизненным циклам. Для этого мы создаем три набора, проходим по саду и размещаем растения в нужном месте.
// Using ordinal() to index into an array. Boo. Set[] plantsByLifecycle = (Set []) new Set[Plant.LifeCycle.values().length]; for (int i=0; i < plantsByLifeCycle.length; i++) { plantsByLifeCycle[i] = new HashSet<>(); } for (Plant p : garden) { plantsByLifeCycle[p.lifeCycle.ordinal()].add(p); } for (int i = 0; i < plantsByLifeCycle.length; i++) { System.out.printf("%s: %s%n", Plant.LifeCycle.values()[i], plantsByLifeCycle[i]); }
Этот код действительно работает, но у него есть различные проблемы. Этот код не компилируется чисто, потому что массивы и дженерики несовместимы друг с другом. Для этого требуется непроверенный бросок. Индексы также не имеют никакого значения, поэтому мы должны придумать свои собственные способы возврата значений. Это также отключение от простого int это возвращено из оригинала и что это на самом деле означает. Это требует от нас быть очень осторожными в этой работе. Единственное преимущество, которое у нас есть, заключается в том, что этот код достаточно быстродействующий.
Теперь давайте рассмотрим EnumMap версия того же кода:
Map> plantsByLifeCycle = new EnumMap<>(Plant.LifeCycle.class); for (Plant.LifeCycle lc : Plant.LifeCycle.values()) { plantsByLifeCycle.put(lc, new HashSet<>()); } for (Plant p : garden) { plantsByLifeCycle.get(p.lifeCycle).add(p); } System.out.println(plantsByLifecycle);
Мы видим здесь преимущества. Никаких непроверенных исключений, более чистый код, нет необходимости маркировать наши собственные значения, и, благодаря факту оптимизированной реализации, мы по-прежнему получаем отличную производительность. Мы можем даже сократить это еще больше, используя потоки.
System.out.println(Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle, () -> new EnumMap<>(LifeCycle.class), toSet())));
Это гораздо более кратко и все же вполне понятно. Разве нам не нужно использовать реализацию Collectors.groupingBy , которая позволяет включать фабрику карт , чтобы она создавала EnumMap вместо традиционной карты реализация, которую это создало бы.
Хотя мы можем привести более сложные примеры, и в книге это делается, приведенный выше пример достаточно поучителен, чтобы понять суть. Это опять же ситуация, когда на самом деле нет никакой пользы в том, чтобы не следовать этому руководству. Мы получаем лучшую безопасность типов, одинаковую производительность и более лаконичный код.
Оригинал: “https://dev.to/kylec32/effective-java-use-enummap-instead-of-ordinal-indexing-3o”