Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flattening a list of elements in Java 8 Optional pipeline

I have a id value which can be null. Then I need to call some service with this id to get a list of trades and fetch the first not null trade from the list.

Currently I have this working code

Optional.ofNullable(id)
    .map(id -> service.findTrades(id))
    .flatMap(t -> t.stream().filter(Objects::nonNull).findFirst())
    .orElse(... default value...); 

Is it possible to implement a line with a flatMap call more elegantly? I don't want to put much logic in one pipeline step.

Initially I expected to implement the logic this way

Optional.ofNullable(id)
    .flatMap(id -> service.findTrades(id))
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(... default value...); 

But Optional.flatMap doesn't allow to flatten a list into a set of it's elements.

like image 356
Andriy Kryvtsun Avatar asked Jun 15 '17 23:06

Andriy Kryvtsun


People also ask

How do you make a list optional in Java?

Third and probably the most useful way of creating an Optional instance is by using the ofNullable() method of java. util. Optional class which allows you to create an Optional object that may hold a null value as shown in the following example: Optional<Person> op = Optional.

What is optional of flatMap Java?

Map. FlatMap. Map will apply the mapping function and if the result is not null – will return Optional describing the result. Flatmap will apply the mapping function and if the result is not null and result already being an optional – flatmap does not wrap with additional Optional.

What is a flatMap in Java 8?

In Java 8 Streams, the flatMap() method applies operation as a mapper function and provides a stream of element values. It means that in each iteration of each element the map() method creates a separate new stream. By using the flattening mechanism, it merges all streams into a single resultant stream.


2 Answers

I don't know if this is elegant or not, but here's a way to transform the optional in a stream before initiating the stream pipeline:

Trade trade = Optional.ofNullable(id)
    .map(service::findTrades)
    .map(Collection::stream)
    .orElse(Stream.empty()) // or orElseGet(Stream::empty)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(... default value...); 

In Java 9, Optional will have a .stream() method, so you will be able to directly convert the optional into a stream:

Trade trade = Optional.ofNullable(id)
    .stream() // <-- Stream either empty or with the id
    .map(service::findTrades) // <-- Now we are at the stream pipeline
    .flatMap(Collection::stream) // We need to flatmap, so that we
    .filter(Objects::nonNull)    // stream the elements of the collection
    .findFirst()
    .orElse(... default value...); 
like image 183
fps Avatar answered Nov 16 '22 10:11

fps


There is a better way to do it by StreamEx

StreamEx.ofNullable(id)
    .flatMap(id -> service.findTrades(id))
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(... default value...); 

I just saw: "As Stuart Marks says it, Rule #4: It's generally a bad idea to create an Optional for the specific purpose of chaining methods from it to get a value.." in the comments under another question:

like image 3
user_3380739 Avatar answered Nov 16 '22 08:11

user_3380739