Java的flatMap在可选整数列表中

时间:2018-12-14 09:06:09

标签: java java-8 java-stream optional

我有以下简化的代码无法编译,我也不明白为什么:

List<Optional<Integer>> list =
    new ArrayList<>();
List<Integer> flattened = 
  list
    .stream()
    .flatMap(i -> i)
    .collect(Collectors.toList());

编译器告诉我:

[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

我承认我不习惯Java,但是必须参加一个项目。我在Scala中嘲笑了这一点,其中list.flatten和等效的list.flatMap(i => i)可以按预期工作:

val list = List(Some(1), Some(2), None)
list.flatten // List(1, 2)

Java的flatMap与众不同吗?

3 个答案:

答案 0 :(得分:14)

应该是:

List<Integer> flattened = 
  list
    .stream()
    .filter (Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList());

您的flatMap需要一个将Stream元素转换为Stream的函数。您应该改为使用map(以提取Optional的值)。此外,您需要过滤掉空的Optional(除非您希望将它们转换为null)。

不进行过滤:

List<Integer> flattened = 
  list
    .stream()
    .map(o -> o.orElse(null))
    .collect(Collectors.toList());

答案 1 :(得分:2)

  

Java的flatMap是否与众不同?

flatMap方法期望提供的函数返回流,但是i -> i不提供该功能。在JDK8中,您需要从Optional然后创建一个流:

 list.stream()
     .flatMap(i -> i.isPresent() ? Stream.of(i.get()) : Stream.empty())
     .collect(Collectors.toList());

或在JDK9中,可以通过以下方式完成:

  list.stream()
      .flatMap(Optional::stream)
      .collect(Collectors.toList());

答案 2 :(得分:2)

Documentation of Stream.flatMap(Function<? extends T, ? extends Stream<? extends R>>)

  

返回一个流,该流包括将流中的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容而得到的结果。将每个映射流的内容放入该流后,将其关闭。 (如果映射的流是null,则使用空流。)   这是一个中间操作。

     

API注意:

     

flatMap()操作的作用是对流的元素进行一对多转换,然后将结果元素展平为新的流。

如您所见,该方法用于获取每个元素,从它们中创建一个 stream ,然后将每个流展平为单个流(由该方法返回)。这样做:

List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
        .flatMap(optional -> optional) // identity function
        .collect(Collectors.toList());

该函数将不返回Optional中的Stream,而不能返回Integer。为了解决这个问题,您需要更改函数以返回Stream包含的内容Optional。您可以执行以下操作,具体取决于您使用的Java版本:

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());

其他选项包括结合使用mapfilterOptional的方法。有关示例,请参见Eran's answer