ПРИХОДИТЕ УЧИТЬСЯ НОВОЙ ПРОФЕССИИ ЛЕТОМ СО СКИДКОЙ ДО 70%Забронировать скидку

Эффективное использование Java 8 Optional с Stream::flatMap

Пройдите тест, узнайте какой профессии подходите и получите бесплатную карьерную консультацию
В конце подарим скидку до 55% на обучение
Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для операции с значениями Optional в потоках Stream рекомендуется использовать метод flatMap. Этот метод конвертирует содержимое типа Optional в поток данных, при этом игнорирует пустые значения. В Java 8 подобный результат достигается отображением комбинации методов Optional::filter и Optional::map, так как метод Optional::stream в этой версии недоступен.

Java
Скопировать код
list.stream()
    .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
    .collect(Collectors.toList());

Таким образом, мы преобразуем Stream<Optional<T>> в Stream<T>, исключая Optional без значений, и извлекаем данные из заполненных Optional.

Пройдите тест и узнайте подходит ли вам сфера IT
Пройти тест

Устранение двойных Optional

Иногда при работе с потоками Optional возникает ситуация с так называемым "двойным Optional", т.е. Optional<Optional<T>>. flatMap эффективно устраняет эту проблему, выравнивая структуру и удаляя пустоту:

Java
Скопировать код
Stream<Optional<Optional<T>>> options = [...]
Stream<T> values = options.flatMap(opt -> opt.orElseGet(Optional::empty).stream());

Продвинутое использование Optional

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

Индивидуальная настройка с использованием вспомогательных методов

Иногда для лучшей читаемости кода целесообразно воспользоваться вспомогательным методом или тернарным оператором:

Java
Скопировать код
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:

Java
Скопировать код
Optional<T> firstPresent = list.stream()
    .reduce(Optional.empty(), (a, b) -> a.isPresent() ? a : b);

Так мы прерываем процесс на первом же найденном непустом значении.

Упрощение – превращение Optional в Stream

Преобразование Optional в Stream может быть упрощено использованием следующего утилитарного метода:

Java
Скопировать код
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 можно сравнить с системой метрополитена для передачи данных:

Markdown
Скопировать код
Маршрут поезда: [Станция: Значение 🚉, Туннель: flatMap 🚇, Станция: Пустой Optional 🚫, Станция: Значение 🚉]

Здесь каждый flatMap действует как тоннель, пропускающий данные из Optional к следующей станции:

Java
Скопировать код
optionalStream.flatMap(Optional::stream)
Markdown
Скопировать код
До: [🚉(Значение), 🚫(Пусто), 🚉(Значение)]
Тоннель: 🚇(flatMap)
После: [🚉(Значение), 🚧(Пропущено), 🚉(Значение)]

Пустые значения типа Optional пропускаются, и данные доходят до своей цели без препятствий.

Сравнение вербальности Java 8 и сжатости Java 9

Метод Optional::stream в версии Java 9 выглядит более кратким, но и Java 8 имеет свои преимущества.

Использование ссылок на методы

Для улучшения читаемости кода предпочтительно использовать ссылки на методы:

Java
Скопировать код
Stream<T> nonEmptyValues = list.stream()
    .flatMap(Optional::map(Stream::of).orElseGet(Stream::empty)::apply)
    .collect(Collectors.toList());

Альтернативный метод – Optional.map и orElseGet

Этот метод демонстрирует другой способ добиться аналогичного результата:

Java
Скопировать код
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 или выбирать значение.

Полезные материалы

  1. Optional (Java Platform SE 8) — официальная документация Oracle для Optional.
  2. Обработка данных с помощью потоков Java SE 8, Часть 1 — детальное руководство по использованию API потоков Java 8.
  3. 26 причин, по которым правильное использование Optional важно – DZone — рекомендации и лучшие практики использования Optional в Java.
  4. Быстрый старт Java SE 8: Лямбда-выражения — введение в лямбда-выражения и потоки Java 8.
  5. Лучшие практики использования и применения Optional в Java 8 – Советы экспертов — советы экспертов на тему лучших практик использования Optional в Java 8.