I have the following bit of simplified code that fails to compile and I don't understand why:
List<Optional<Integer>> list =
new ArrayList<>();
List<Integer> flattened =
list
.stream()
.flatMap(i -> i)
.collect(Collectors.toList());
The compiler tells me:
[ERROR] ... incompatible types: cannot infer type-variable(s) R
[ERROR] (argument mismatch; bad return type in lambda expression
[ERROR] Optional<Integer> cannot be converted to Stream<? extends R>)
[ERROR] where R,T are type-variables:
[ERROR] R extends Object declared in method <R>flatMap(Function<? super T,? extends Stream<? extends R>>)
[ERROR] T extends Object declared in interface Stream
I admit I'm not used to Java but I have to for a project. I mocked this in Scala where list.flatten
and the equivalent list.flatMap(i => i)
work just as expected:
val list = List(Some(1), Some(2), None)
list.flatten // List(1, 2)
Is Java's flatMap
different?
The flatMap() method of optionals allows you to transform the optional if it has a value, or do nothing if it is empty. This makes for shorter and more expressive code than doing a regular unwrap, and doesn't require you to change your data type.
The standard solution is to use the Stream. flatMap() method to flatten a List of Lists. The flatMap() method applies the specified mapping function to each element of the stream and flattens it.
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.
You should use map instead (to extract the value of the Optional ). In addition, you need to filter out empty Optional s (unless you wish to transform them to null s). Show activity on this post. Is Java's flatMap different? The flatMap method expects the function supplied to return a stream but i -> i doesn't provide that.
As you all know, the Optional object is a generic object, its data type can be any object, and therefore, we can meet the case that its data type is also an Optional object, such as: Optional<Optional<String>>. To resolve this problem, Java introduces us to the flatMap () method so that Optional’s data type is simpler: Optional<String> for example.
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 should be:
List<Integer> flattened =
list
.stream()
.filter (Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
Your flatMap
expects a function that transforms a Stream
element to a Stream
. You should use map
instead (to extract the value of the Optional
). In addition, you need to filter out empty Optional
s (unless you wish to transform them to null
s).
Without the filtering:
List<Integer> flattened =
list
.stream()
.map(o -> o.orElse(null))
.collect(Collectors.toList());
Is Java's flatMap different?
The flatMap
method expects the function supplied to return a stream but i -> i
doesn't provide that. In JDK8 you'll need to create a stream from the Optional then flatten:
list.stream()
.flatMap(i -> i.isPresent() ? Stream.of(i.get()) : Stream.empty())
.collect(Collectors.toList());
or in JDK9, in can be done as:
list.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
Documentation of Stream.flatMap(Function<? extends T, ? extends Stream<? extends R>>)
:
Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is
null
an empty stream is used, instead.) This is an intermediate operation.API Note:
The
flatMap()
operation has the effect of applying a one-to-many transformation to the elements of the stream, and then flattening the resulting elements into a new stream.
As you can see, the method is used to take each element, create a stream from them, then flatten each stream into a single stream (which is returned by the method). So doing:
List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
.flatMap(optional -> optional) // identity function
.collect(Collectors.toList());
Won't work as the function returns an Optional
not a Stream
of Integer
s. To solve this you need to change the function to return a Stream
of what the Optional
contains. Depending on the version of Java you're using you can do:
Java 9+,
List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
Java 8,
// There are different ways to convert an Optional into a Stream using
// flatMap, this is just one option. Holger shows other ways in the comments.
List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
.flatMap(optional -> optional.isPresent() ? Stream.of(optional.get()) : Stream.empty())
.collect(Collectors.toList());
Other options include using map
combined with filter
or methods of Optional
. See Eran's answer for examples.
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