Коллекторы Stream API простыми словами

Руслан, backend-разработчик Инфомаксимум, кратко о коллекторах

Коллекторы Stream API простыми словами

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

Такое понятие как Stream API вошло в понимание Java-разработчиков с приходом 8 версии JDK. И в плане использования «стримы» оказали довольно сильное влияние на будущий код. Многие по достоинству оценили лаконичность получаемого кода, когда можно не писать громоздкие блоки for или while.

Однако часто возникают пробелы в понимании того, как работают стримы. Ведь при их применении могут возникать различного рода overhead’ы, а то и вовсе проблемы с реализацией необходимой логики. Известно, что стримы содержат операции, но хотелось бы остановиться на одной из них – терминальной операции Collector. Это часто употребляемый шаг в стримах, однако мало кто задумывается о нюансах его работы.

Рассмотрим особенности работы коллекторов и разберем несколько наиболее часто встречающихся примеров.

Начнем с того, что коллекторы – это объекты, разработанные для какой-либо агрегации потоковых данных. Поток, пройдя через цепочку отображений, фильтров и других не терминальных операций, по итогу представляет из себя поток объектов. Их необходимо либо собрать воедино, либо выбрать какой-то один, а может быть, и вообще получить новый информационный объект на основе каких-то характеристик. В этой точке и могут быть применены коллекторы.

На чем основана агрегация в коллекторах? В их основу заложены 4 основополагающих принципа:

  • Supplier()

Нужен для создания новых результирующих контейнеров. Ведь «прилетающие» в потоке данные необходимо куда-то складывать;

  • Accumulator()

Занимается сложением, или аккумуляцией данных. Аккумулятор складирует приходящие данные во временные контейнеры;

  • Combiner()

Combiner объединяет полученные временные контейнеры в более крупный, что по итогу приводит к получению агрегированных данных;

  • Finisher()

Опционально, но не обязательно, может присутствовать Finisher, который проведет post-обработку полученного глобального контейнера и вернет уже результат в качестве итога работы коллектора.

Таким образом, реализовав необходимые интерфейсы описанных четырех принципов, можно создать сколь угодно сложный коллектор, который будет выполнять необходимую агрегацию данных, если вы не смогли найти готовое решение из уже реализованных в Java Core.

Реализация кастомного коллектора воплощена в методе of:

Коллекторы Stream API простыми словами

Рассмотрим часть коллекторов, уже существующих в Java Core, на базе утилитного класса Collectors.

Наиболее часто употребляемые коллекторы агрегации данных в коллекции:

Соберет данные в List<> на базе ArrayList<>:

Коллекторы Stream API простыми словами

Соберет данные в Set<> на базе HashSet<>:

Коллекторы Stream API простыми словами

Соберет данные в Map<> на базе HashMap<>:

Коллекторы Stream API простыми словами

Соберет данные в коллекцию, указанную разработчиком:

Коллекторы Stream API простыми словами

Помимо чистой агрегации данных в массивы и коллекции, существует серия коллекторов для группировки данных по какому-либо признаку. Например, groupingBy по переданному классификатору classifier создаст Map<>, в котором в качестве key будет результат классификатора, а value – агрегация от вторичного коллектора downstream. Вторичным коллектором может выступать абсолютно любой коллектор, главное понимать, что на вход этот коллектор пример тот же поток данных, что и классификатор.

Коллекторы Stream API простыми словами

Следующий на очереди – коллектор отображения. Он произведет отображение входящего потока в поток отображения и соберет всё вторичным коллектором downstream. Часто заменяется обычным отображением через промежуточную операцию map:

Коллекторы Stream API простыми словами

Также существует целая серия коллекторов по математическим операциям суммирования, усреднения, поиска минимального и максимального числа, какой-либо редукции, объединения строк и т.д. Стоит отметить отдельные коллекторы для многопоточных реализаций. Например:

Коллекторы Stream API простыми словами

Он соберет данные в ConcurrentMap<> на базе такого же HashMap<>, где в качестве key будет результат от работы классификатора classifier, а в качестве value – агрегация от уже описанного выше коллектора toList().

Напоследок еще один любопытный коллектор:

Коллекторы Stream API простыми словами

Он произведет ту самую post-обработку результата коллектора downstream функционалом finisher’а, о котором было упомянуто ранее, а результатом будет его итог.

Рассмотрим практический пример конструирования собственного коллектора через метод of, указанный в начале статьи. Соберем проект на Gradle. Для удобства в зависимость добавим библиотеку Guava:

Коллекторы Stream API простыми словами

Был смоделирован тестовый поток целочисленных данных от 0 до 99, данные были собраны в LinkedList, и в finisher’е проведена post-обработка по подсчету четных цифр. Так можно создать абсолютно любой коллектор без ограничения по логике.

На этом завершим краткий обзор коллекторов простыми словами.

Успешной разработки на Java Stream API.

Буду рад ответить на вопросы, если будут :)

33
2 комментария

Крутяк. Лайк.

Ответить

Спасибо)

1
Ответить