I'm trying to flatMap
Optional
s in Java. Here is a simplified example:
List<String> x = Arrays.asList("a", "b", "c");
List<String> result = x.stream().flatMap((val) -> val.equals("b") ? Optional.empty() : Optional.of(val)).collect(Collectors.toList());
I get this error message from the compiler:
Error:(10, 27) java: incompatible types: no instance(s) of type variable(s) T exist so that java.util.Optional<T> conforms to java.util.stream.Stream<? extends R>
What's wrong? Here is an example of what I'm trying to achieve in Scala:
List("a", "b", "c").flatMap(x => if (x == "b") None else Some(x))
It returns:
res2: List[String] = List(a, c)
as expected.
How do I convert this to Java so that it compiles?
Introduction Before diving deep into the practice stuff let us understand the flatMap method and Optional class in java programming. This method is used to convert a stream of collections to a stream of objects. It is an intermediate method and returns a stream as the output value.
We can use a flatMap () method on a stream with the mapper function List::stream. On executing the stream terminal operation, each element of flatMap () provides a separate stream. In the final phase, the flatMap () method transforms all the streams into a new stream.
It makes total sense for Java. The class invariant is that an Optional can never hold a null value, so either the map should fail or it should return an empty Optional. The second seems the better choice. In stricter functional programming languages (aka Scala), this doesn’t happen.
Following are few facts about flatMap operation. Optional.flatMap (mapper) takes a mapping Function as an input, which accept reference of type T and return Optional<U>. If the value of T is null, flatMap returns empty Optional. Compared to map (Function), flatMap does not wrap it with an additional Optional.
flatMap
is expected to map an element of the input Stream
into a different Stream
. Therefore it must return a Stream
and not an Optional
.
Therefore, you should do something like this :
List<String> x = Arrays.asList("a", "b", "c");
List<Optional<String>> result =
x.stream()
.flatMap((val) ->
val.equals("b") ? Stream.of(Optional.empty()) :
Stream.of(Optional.of(val)))
.collect(Collectors.toList());
Note that if your goal is simply to get rid of some of the values ("b" in your example), you don't need to use Optional at all. You can just filter the Stream :
List<String> result =
x.stream()
.filter (val -> !val.equals("b"))
.collect(Collectors.toList());
This way you don't need flatMap
and your output is a List<String>
instead of a List<Optional<String>>
.
As Holger commented, the solution that returns a Stream
of Optional
s can be simplified by using map
instead of flatMap
, since each element is mapped into a single Optional
:
List<String> x = Arrays.asList("a", "b", "c");
List<Optional<String>> result =
x.stream()
.map((val) -> val.equals("b") ? Optional.empty() : Optional.of(val))
.collect(Collectors.toList());
There is no need to deal with Optional
here.
The simplest straight-forward solution is to use filter
List<String> result = x.stream()
.filter(val -> !val.equals("b"))
.collect(Collectors.toList());
If you insist on using flatMap
, you should simply use Stream
instead of Optional
:
List<String> result = x.stream().flatMap(
val -> val.equals("b")? Stream.empty(): Stream.of(val))
.collect(Collectors.toList());
If you have to deal with an operation that unavoidably produces an Optional
, you will have to convert it to a Stream
for using Stream.flatMap
:
List<String> result = x.stream()
.map(val -> val.equals("b") ? Optional.<String>empty() : Optional.of(val))
.flatMap(o->o.map(Stream::of).orElse(Stream.empty()))
.collect(Collectors.toList());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With