Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java's flatMap on list of list of optional integers

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?

like image 654
Max Power Avatar asked Dec 14 '18 09:12

Max Power


People also ask

What is flatMap in optional?

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.

How do you flatten a stream List?

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.

What is flatMap and optional in Java programming?

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.

How do I get the value of an optional in Java?

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.

What is the data type of an optional object in Java?

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.

How to use flatMap () method on a stream in Java?

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.


3 Answers

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 Optionals (unless you wish to transform them to nulls).

Without the filtering:

List<Integer> flattened = 
  list
    .stream()
    .map(o -> o.orElse(null))
    .collect(Collectors.toList());
like image 57
Eran Avatar answered Oct 18 '22 01:10

Eran


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());
like image 26
Ousmane D. Avatar answered Oct 18 '22 02:10

Ousmane D.


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 Integers. 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.

like image 3
Slaw Avatar answered Oct 18 '22 03:10

Slaw