Эффективное использование Java 8 Optional с Stream::flatMap
Быстрый ответ
Для операции с значениями Optional в потоках Stream рекомендуется использовать метод flatMap. Этот метод конвертирует содержимое типа Optional в поток данных, при этом игнорирует пустые значения. В Java 8 подобный результат достигается отображением комбинации методов Optional::filter и Optional::map, так как метод Optional::stream в этой версии недоступен.
list.stream()
.flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
.collect(Collectors.toList());
Таким образом, мы преобразуем Stream<Optional<T>>
в Stream<T>
, исключая Optional без значений, и извлекаем данные из заполненных Optional.
Устранение двойных Optional
Иногда при работе с потоками Optional возникает ситуация с так называемым "двойным Optional", т.е. Optional<Optional<T>>
. flatMap эффективно устраняет эту проблему, выравнивая структуру и удаляя пустоту:
Stream<Optional<Optional<T>>> options = [...]
Stream<T> values = options.flatMap(opt -> opt.orElseGet(Optional::empty).stream());
Продвинутое использование Optional
Если требуются более сложные способы настройки Optional в потоках, следует рассмотреть следующие подходы.
Индивидуальная настройка с использованием вспомогательных методов
Иногда для лучшей читаемости кода целесообразно воспользоваться вспомогательным методом или тернарным оператором:
public <T> Stream<T> optionalToStream(Optional<T> opt) {
return opt.map(Stream::of).orElseGet(Stream::empty);
}
list.stream()
.flatMap(this::optionalToStream)
.collect(Collectors.toList());
Сокращение Optional
Следующий метод reduce помогает найти первое непустое значение среди последовательности значений типа Optional:
Optional<T> firstPresent = list.stream()
.reduce(Optional.empty(), (a, b) -> a.isPresent() ? a : b);
Так мы прерываем процесс на первом же найденном непустом значении.
Упрощение – превращение Optional в Stream
Преобразование Optional в Stream может быть упрощено использованием следующего утилитарного метода:
public static <T> Stream<T> flatStream(Optional<T> mayBe) {
return mayBe.map(Stream::of).orElseGet(Stream::empty);
}
Stream<T> stream = optionalsList.stream()
.flatMap(CustomOptional::flatStream);
Применение этого подхода помогает централизовать управление Optional, что делает код нагляднее и упрощает его поддержку.
Визуализация
Использование Optional с Stream::flatMap в Java 8 можно сравнить с системой метрополитена для передачи данных:
Маршрут поезда: [Станция: Значение 🚉, Туннель: flatMap 🚇, Станция: Пустой Optional 🚫, Станция: Значение 🚉]
Здесь каждый flatMap действует как тоннель, пропускающий данные из Optional к следующей станции:
optionalStream.flatMap(Optional::stream)
До: [🚉(Значение), 🚫(Пусто), 🚉(Значение)]
Тоннель: 🚇(flatMap)
После: [🚉(Значение), 🚧(Пропущено), 🚉(Значение)]
Пустые значения типа Optional пропускаются, и данные доходят до своей цели без препятствий.
Сравнение вербальности Java 8 и сжатости Java 9
Метод Optional::stream в версии Java 9 выглядит более кратким, но и Java 8 имеет свои преимущества.
Использование ссылок на методы
Для улучшения читаемости кода предпочтительно использовать ссылки на методы:
Stream<T> nonEmptyValues = list.stream()
.flatMap(Optional::map(Stream::of).orElseGet(Stream::empty)::apply)
.collect(Collectors.toList());
Альтернативный метод – Optional.map и orElseGet
Этот метод демонстрирует другой способ добиться аналогичного результата:
list.stream()
.map(opt -> opt.orElseGet(() -> null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
Креативность во время работы с Java 8
В процессе работы в Java 8 можно проявлять креативность:
- Комбинация методов map, filter, findFirst и flatMap позволяет получить функциональность аналогичную Optional::stream.
- Сложные операции можно обернуть в пользовательские утилиты для работы с Optional в потоках.
- Метод reduce помогает обобщать данные с использованием Optional или выбирать значение.
Полезные материалы
- Optional (Java Platform SE 8) — официальная документация Oracle для
Optional
. - Обработка данных с помощью потоков Java SE 8, Часть 1 — детальное руководство по использованию API потоков Java 8.
- 26 причин, по которым правильное использование Optional важно – DZone — рекомендации и лучшие практики использования
Optional
в Java. - Быстрый старт Java SE 8: Лямбда-выражения — введение в лямбда-выражения и потоки Java 8.
- Лучшие практики использования и применения Optional в Java 8 – Советы экспертов — советы экспертов на тему лучших практик использования
Optional
в Java 8.