Иногда преобразования между структурами данных могут стать повторяющимися или подверженными ошибкам. Если вы работаете с потоками в Java, вам может потребоваться захватить список, преобразовать его в поток и сопоставить его с другой структурой данных.

Есть очень простой способ сделать такие преобразования благодаря Stream и Collectors API от Java. В этой статье я расскажу о различных способах преобразования потоков в другие структуры данных.

Вот некоторые из преобразований структур данных, о которых я расскажу:

  • Список
  • карта
  • Группировка карт
  • Установленный
  • Разбиение на разделы
  • Группировка элементов подсчета
  • Добавление чисел
  • Средние числа

Я буду использовать в качестве примера класс под названием Car, который имеет свойства id: int, type: String и numberOfSeats: int. Для всех примеров я буду использовать следующие два объекта.

Список

Это простейший вариант использования, когда вам может потребоваться просто преобразовать поток в список. Для этого вам нужно будет передать Collectors.toList() методу .collect.

карта

Чтобы преобразовать поток в карту, ​​вам нужно вызвать метод collect и передать его в качестве аргумента Collectors.toMap. Метод toMap принимает два аргумента.

Первым будет лямбда, возвращающая ключ карты, вторым аргументом будет лямбда со значением для этого ключа в карте.

Важное замечание: если в конечном итоге у вас будет несколько элементов с одним и тем же ключом, будет выдано IllegalStateException, говорящее о том, что существует повторяющийся ключ.

Следите за обновлениями! В следующем разделе я расскажу, как обойти эту проблему.

Группировка карт

В этом случае вам нужно будет вызвать метод groupingBy, потому что вы хотите иметь карту, но у вас может оказаться более одного элемента на ключ. Должно произойти небольшое изменение, так как теперь вы не будете возвращать Map<Integer, Car>, вам нужно будет вернуть список автомобилей для каждого ключа Map<Integer, List<Car>>.

Чтобы продолжить и выполнить этот тип группировки, вам необходимо передать вызов Collectors.groupingBy методу collect. Метод groupingBy принимает в качестве аргумента лямбду, которая возвращает ключ для каждого элемента на карте.

Установленный

В этом случае следите за нашим потоком, поскольку я намеренно добавил к нему car2 второй раз. Преобразование ваших данных в набор станет полезным, если вы хотите иметь уникальную коллекцию ваших элементов. При преобразовании потока в набор будут отброшены все повторяющиеся объекты.

Чтобы преобразовать поток в набор, вам нужно вызвать метод collect и передать в качестве аргумента вызов Collectors.toSet() без параметров.

Разбиение на разделы

Это очень эффективный способ группировки элементов по условию. В этом примере мы разделим данные на автомобили с более чем четырьмя сиденьями и автомобили с четырьмя сиденьями или меньше. Результатом будет карта с логическим значением в качестве ключа, а значением будет список объектов, соответствующих критериям для этого ключа.

Напоминаем, что в нашем текущем наборе данных есть одна машина с четырьмя сиденьями и одна машина с семью сиденьями. Нам нужно будет использовать вызов метода partitioningBy, который будет иметь значение true для автомобиля с семью сиденьями и false для автомобиля с четырьмя сиденьями.

Следующий JSON иллюстрирует ожидаемый результат этого разделения.

Если бы у нас была другая машина с пятью местами, эта машина была бы добавлена ​​в список, соответствующий клавише true из объекта выше.

Чтобы вы могли разделить свои данные, вам нужно будет вызвать метод Collectors.partitioningBy и передать его в качестве аргумента методу collect. Метод partitioningBy принимает в качестве аргумента лямбду, возвращающую логическое выражение.

Подсчитать за элемент

Вам нужно знать, сколько элементов приходится на конкретное свойство ваших объектов? Предположим, вы хотите узнать, сколько автомобилей приходится на количество мест. Для этого можно использовать методы groupingBy и counting. Результатом будет карта, где ключом является свойство, по которому вы группируете (количество мест), а значение имеет тип long с общим количеством чисел на элемент для этого ключа.

Для этого вам нужно вызвать Collectors.groupingBy, который будет принимать два аргумента. Первым будет лямбда, возвращающая свойство, по которому вы хотите сгруппировать. Второй аргумент примет вызов Collectors.counting(). Все это будет передано в collect вызов, который вы сделали в потоке.

Сумма значений

Вы также можете легко суммировать по определенному свойству из элементов вашего потока. Для этого вам нужно будет передать вызов Collectors.summingInt вашему методу collect. В качестве аргумента метод summingInt примет лямбду, в которой вы возвращаете свойство, по которому хотите подвести итог.

Вы также можете вычислить среднее значение вместо добавления элементов, вызвав Collectors.averagingInt. Аналогичные операции доступны для типов данных double и long.

Эти операции будут

  • Collectors.averagingDouble
  • Collectors.averagingLong
  • Collectors.summingDouble
  • Collectors.summingLong.

Я надеюсь, что эта статья помогла вам лучше познакомиться с возможностями, предлагаемыми API Collectors и Stream. Наслаждаться!