Статьи

JDK 8 потоков и группировка

Я написал о мощных функциях использования потоков JDK 8 с коллекциями Java в функциональности пост- потоковых коллекций в JDK 8 . Я не рассматривал использование операции сокращения groupingBy Collector в этом посте, а также группировку адресов в этом посте.

В примерах, приведенных в этом посте, будет продемонстрировано, как объединить потоки, поддерживаемые коллекцией, с коллекционерами groupingBy для реорганизации данных базовой коллекции в группы, предписанные предоставленной классификацией. Эти примеры основаны на классе Movie классах Set of Movie описанных в моем предыдущем посте « Потоковая функциональность коллекций в JDK 8» .

В следующем листинге кода показано, как можно использовать простое утверждение для группировки предоставленного Set Movie в Map оценок фильмов (ключ) к фильмам с таким рейтингом (значением). MpaaRating Collector предоставляет эту Map как карту типа ключа (в данном случае MpaaRating ) в List типов сгруппированных объектов (в данном случае Movie ).

01
02
03
04
05
06
07
08
09
10
/**
 * Demonstrate use of JDK 8 streams and Collectors.groupingBy to
 * group movies by their MPAA ratings.
 */
private static void demonstrateGroupingByRating()
{
   final Map<MpaaRating, List<Movie>> moviesByRating =
      movies.stream().collect(groupingBy(Movie::getMpaaRating));
   out.println("Movies grouped by MPAA Rating: " + moviesByRating);
}

В только что показанном примере (и в примерах, которые следуют в этом посте) статический импорт java.util.stream.Collectors.groupingBy позволяет мне НЕ выполнять область вызовов groupingBy с именем класса Collectors . Этот простой фрагмент кода группирует фильмы по их рейтингам с возвращенным ключом сопоставления Map рейтинга фильмов с List фильмов, связанных с каждым рейтингом. Вот пример вывода, когда предоставленный набор Movie такой же, как в моем предыдущем сообщении.


Фильмы сгруппированы по рейтингу MPAA: {PG13 = [Фильм: Начало (2010), SCIENCE_FICTION, PG13, 13], R = [Фильм: Побег из Шоушенка (1994), DRAMA, R, 1], PG = [Фильм: Рейдеры Потерянный Ковчег (1981), ДЕЙСТВИЕ, PG, 31, Фильм: Назад в будущее (1985), SCIENCE_FICTION, PG, 49, Фильм: Звездные войны: Эпизод V — Империя наносит ответный удар (1980), SCIENCE_FICTION, PG, 12 ]}

Конкретное использование только что продемонстрированной возможности заключается в создании Map уникальных ключей для объектов в Collection для объекта этой Collection с этим ключом. Это может быть полезно, например, когда нужно многократно и быстро искать объекты по карте, но при этом вместо Map предоставляется объект, представляющий интерес в Set или List . Притворяясь, что фильмы имеют уникальные названия (они подходят только для моего небольшого набора), такая функциональность может быть реализована, как показано в следующем листинге кода.

01
02
03
04
05
06
07
08
09
10
/**
  * Demonstrate use of JDK 8 streams and Collectors.groupingBy to
  * group movies by their title.
  */
private static void demonstrateGroupingByTitle()
{
   final Map<String, List<Movie>> moviesByTitle =
      movies.stream().collect(groupingBy(Movie::getTitle));
   out.println("Movies grouped by title: " + moviesByTitle);
}

Предполагая, что заголовок уникален для каждого фильма в исходной коллекции, приведенный выше код обеспечивает сопоставление заголовка фильма с одноэлементным List содержащим только фильм, для которого этот заголовок применим. Любой клиент, желающий быстро найти фильм по его названию, может вызвать moviesByTitle.get(String).get(0) чтобы получить полный объект Movie соответствующий этому заголовку. Результат выполнения этого с моим простым набором фильмов показан ниже.


Фильмы, сгруппированные по названию: {Выкуп Шоушенка = [Фильм: Выкуп Шоушенка (1994), DRAMA, R, 1], Звездные войны: Эпизод V — Империя наносит ответный удар = [Фильм: Звездные войны: Эпизод V — Империя наносит удар] Back (1980), SCIENCE_FICTION, PG, 12], Back to the Future = [Фильм: Назад в будущее (1985), SCIENCE_FICTION, PG, 49], Raiders of the Lost Ark = [Фильм: Рейдеры Lost Ark ( 1981), ACTION, PG, 31], Inception = [Фильм: Inception (2010), SCIENCE_FICTION, PG13, 13]}

Можно сгруппировать по двум различным характеристикам. Это позволяет сгруппировать Collection по одному признаку, а затем подгруппировать каждую из этих групп по второму признаку. Например, следующий код перечисляет фильмы по рейтингу, а затем по жанру.

01
02
03
04
05
06
07
08
09
10
/**
 * Demonstrate use of JDK 8 streams and cascaded groupingBy
 * to group movies by ratings and then by genres within ratings.
 */
private static void demonstrateGroupingByRatingAndGenre()
{
   final Map<MpaaRating, Map<Genre, List<Movie>>> moviesByRatingAndGenre =
      movies.stream().collect(groupingBy(Movie::getMpaaRating, groupingBy(Movie::getGenre)));
   out.println("Movies by rating and genre: " + moviesByRatingAndGenre);
}

Список кодов только что показал, сначала группирует основные фильмы по рейтингу, а затем снова группирует каждый фильм с определенной группой оценок, но на этот раз по жанрам. Другими словами, мы получаем двухуровневые группы фильмов по рейтингу и жанру. Вывод на мой простой набор фильмов показан далее.


Фильмы по рейтингу и жанру: {PG13 = {SCIENCE_FICTION = [Фильм: Начало (2010), SCIENCE_FICTION, PG13, 13]}, R = {DRAMA = [Фильм: Побег из Шоушенка (1994), DRAMA, R, 1]} , PG = {SCIENCE_FICTION = [Фильм: Назад в будущее (1985), SCIENCE_FICTION, PG, 49, Фильм: Звездные войны: Эпизод V — Империя наносит ответный удар (1980), SCIENCE_FICTION, PG, 12], ДЕЙСТВИЕ = [Фильм : Рейдеры Затерянного Ковчега (1981), ACTION, PG, 31]}}

Сборщик groupingBy позволяет сгруппировать элементы List или Set в карту с характеристикой группировки в качестве ключа и объектами, принадлежащими каждой группе в List связанной с этим ключом характеристики группировки. Это позволяет использовать все преимущества Map , включая использование некоторых удобных методов на Map , которые были представлены в JDK 8 .

Ссылка: JDK 8 Streams и группировка от нашего партнера JCG Дастина Маркса в блоге Inspired by Actual Events .