Explain Codes LogoExplain Codes Logo

Using Java 8's Optional with Stream::flatMap

java
optional-engineering
streaming
java-8
Alex KataevbyAlex Kataev·Dec 18, 2024
TLDR

To handle Optional values within a Stream, use flatMap. It converts non-empty Optionals into a Stream of their values and discards empty ones. In Java 8, you can achieve the same with Optional::filter and Optional::map. Optional::stream isn't available here.

list.stream() .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty)) // It's like making juice without the pulps! .collect(Collectors.toList()); // Bring back the juice buddy!

Here, Stream<Optional<T>> gets converted to Stream<T>. The empty Optional elements are omitted and the values contained in the non-empty Optionals are unwrapped.

Busting Double Optionals

Dealing with streams of Optional objects can often result in double optionals (e.g., Optional<Optional<T>>). But flatMap can combat this effectively by flattening and filtering out the unwanted emptiness:

Stream<Optional<Optional<T>>> options = // This is Deception Inception Stream<T> values = options.flatMap(opt -> opt.orElseGet(Optional::empty).stream()); // Double trouble handled!

Going Beyond - Advanced Optional

While the simple one-liner works, it's often helpful to control how and when to deal with Optional values inside our streams. Let's explore more!

Customizing with Expressive Handlers

At times, using a ternary operator or a helper function can make your gospel much more expressive:

public <T> Stream<T> optionalToStream(Optional<T> opt) { return opt.map(Stream::of).orElseGet(Stream::empty); // My helper function to save the day! } list.stream() .flatMap(this::optionalToStream) // Boom! It's a flat world now! .collect(Collectors.toList());

Reducing Optionals - The Smart Way

To fetch the first non-empty value from a bunch of Optionals, capitalize on the reduce method:

Optional<T> firstPresent = list.stream() .reduce(Optional.empty(), (a, b) -> a.isPresent() ? a : b); // Early bird gets the worm! So why wait?

This halts the reduction procedure as soon as the first non-empty Optional is found!

Super Streamlining - Optional to Stream Mapping

Use a util method to handle Optional-to-Stream conversion smoothly that keeps your code squeaky clean:

public static <T> Stream<T> flatStream(Optional<T> mayBe) { return mayBe.map(Stream::of).orElseGet(Stream::empty); // Decode Optional, encrypt Stream. You know, just spy stuff! } Stream<T> stream = optionalsList.stream() .flatMap(CustomOptional::flatStream); // Go custom, why stay normal?

This provides a central point for Optionals management, making code maintenance a piece of cake!

Java 8 Verbosity Vs Java 9 Brevity

Java 9's Optional::stream is tantalizingly brief, but Java 8 has its own charm too!

Leveraging Method References

Use method references to craft more readable poems in Java:

Stream<T> nonEmptyValues = list.stream() .flatMap(Optional::map(Stream::of).orElseGet(Stream::empty)::apply) // Goodbye verbosity, hello brevity! .collect(Collectors.toList());

The Alternate Route - Optional.map with orElseGet

For an equivalent result with a different syntax:

list.stream() .map(opt -> opt.orElseGet(() -> null)) // Do you Opt or do you Null? .filter(Objects::nonNull) // No nulls allowed in this party! .collect(Collectors.toList());

Java 8 Creativity - No Java 9 Envy

Java 8 challenges can be tackled creatively:

  • map, filter, findFirst and flatMap combined can play Optional::stream pretty well.
  • Complex logic can be wrapped into custom util methods to deal with stream operations involving Optionals.
  • reduce can be used with Optional for data summarization or selection.