【问题标题】:Java's flatMap on list of list of optional integersJava的flatMap在可选整数列表上
【发布时间】:2018-12-14 09:06:08
【问题描述】:

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

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 =&gt; i) 按预期工作:

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

Java 的flatMap 有什么不同吗?

【问题讨论】:

  • 这取决于你使用的java版本,见this post
  • 您不能“平面映射”单个对象或单个集合。 Flatmap 将流组合成一个流。反过来,这些流可以来自在每个元素上使用 .stream() 的集合,如下所示:.flatMap(i -&gt; i.stream())

标签: java java-8 java-stream optional


【解决方案1】:

应该是:

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

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

没有过滤:

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

【讨论】:

  • “除非您希望将它们转换为空值” - 对空的可选项调用 get() 会引发异常。
  • @JaroslawPawlakJaros 这就是为什么我没有在任何空的 Optional 上调用 get。我在第二个解决方案中使用了 orElse
  • 我的意思是,听起来您可以跳过filter 并直接执行map(Optional::get),它会转换为null。你的回答很好,为了避免混淆,我只想说它略有不同。
【解决方案2】:

Java 的 flatMap 有什么不同吗?

flatMap 方法期望提供的函数返回一个流,但 i -&gt; 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());

【讨论】:

  • 但是stream() 不会从列表中创建一个流吗?
  • @MaxPower 列表的stream 方法是的。如果值存在,则 Optional 的 stream 方法创建一个元素的流,否则创建一个空流。
【解决方案3】:

DocumentationStream.flatMap(Function&lt;? extends T, ? extends Stream&lt;? extends R&gt;&gt;):

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

API 说明:

flatMap() 操作具有对流的元素应用一对多转换的效果,然后将结果元素展平为新的流。

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

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

当函数返回 Optional 而不是 StreamIntegers 时将不起作用。要解决此问题,您需要更改函数以返回 Optional 包含的内容的 Stream。根据您使用的 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());

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

【讨论】:

  • .flatMap(optional -&gt; optional.map(Stream::of).orElseGet(Stream::empty))。甚至可以使用.flatMap(optional -&gt; optional.map(Stream::of).orElse(null))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
  • 2017-09-08
  • 1970-01-01
  • 2011-07-19
  • 2021-04-08
  • 1970-01-01
相关资源
最近更新 更多