Java 22: изменения в Stream API (с плохим API)19 марта выйдет Java 22. Там появится интересная превью фича Stream Gatherers. Коротко расскажу, что это, зачем нужно, и что с ней не так.
Начнём. Код со Stream API делится на 3 части:
▫️ источник данных
▫️ промежуточные операции:
map, filter, flatMap▫️ терминальная операция:
collect, count, findAnyПромежуточных операций немного, но они легко покрывают большую часть задач.
Иногда их недостаточно. Например, хочется сделать
distinct по какому-то признаку и дальше работать с исходными объектами:
clients.stream()
.distinct(Client::getBonusType)
.map(Client::getId).forEach(…)
Но это невозможно,
distinct работает только по
equals. В итоге надо либо извращаться, либо переписывать на for.
В Java 22 в Stream API появится универсальный метод
gather. Туда можно передать логику преобразований с любыми отношениями - 1:1, 1:N, N:1, N:N. Синтаксис сложный, но круто, что такая возможность появилась.
Второе нововведение. Для
collect есть готовые статические методы в классе
Collectors, а для
gather появится
Gatherers с новыми методами. Самое полезное — два метода по работе с окнами:
🪟
windowFixed — поделить стрим на подмножества заданного размера
[1,2,3,4,5,6] → windowFixed(3) → [1,2,3], [4,5,6]
🪟
windowSliding — подмножества с пересечением одного элемента
[1,2,3,4,5,6,7] → windowSliding(3) → [1,2,3], [3,4,5], [5,6,7]
Что не так c методом gather?1️⃣ Многословность
Та же ситуация, что и с
collect. Чтобы разбить список на окна, придётся написать
list.stream()
.gather(Gatherers.windowFixed(2))
.collect(Collectors.toList())
Даже со статическим импортом выглядит не очень. Хочется писать без лишних слов:
list.stream()
.windowFixed(2)
.toList()
Да, исходный код Stream API сложный, и его тяжело расширять. Да, через статические методы реализация получится проще. Но не вижу ничего невозможного, чтобы сделать новые методы лаконичными, без приставки "gather(Gatherers"
2️⃣ Странные новые методы
Начнём с оконных.
windowSliding делает пересечение только по одному элементу. Зачем нужно это ограничение — непонятно. Так же непонятно, зачем делать два отдельных метода
windowFixed и
windowSliding.
За образец можно взять Kotlin:
list.windowed(5,2)
Первый параметр задаёт размер окна, второй — шаг, с которым идём по списку. Удобно и понятно.
Ещё в Gatherers появятся три странных метода:
fold,
mapConcurrent и
scan. С первого взгляда непонятно, зачем они нужны, очень уж специфичны.
В целом криминала в gather/Gatherers нет, жить можно. Но важный навык разработчика — замечать слабые места в своих и чужих решениях. Этот навык нужно развивать, для этого и нужен этот пост:)
Что ещё почитать:
🔥
Серия постов про коллекторы. Там же я рассказала, почему в стримах используется отдельный метод
collect, а не просто
toList()🔥
Новые методы Stream API в Java 16 🔥
Критикую метод HashMap в Java 20. Хотя сам метод маленький, он показывает серьёзную ошибку проектирования API