Java的平面映射在可选整数列表列表上



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

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不同吗?

应该是:

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

Java的flatMap不同吗?

flatMap方法希望提供的函数返回流,但i -> i没有提供。在JDK8中,您需要从Optional创建一个流,然后压平:

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

或者在JDK9中,in可以做为:

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

Stream.flatMap(Function<? extends T, ? extends Stream<? extends R>>):的文档

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

API注:

flatMap()操作的效果是对流的元素应用一对多变换,然后将得到的元素平坦化为新的流。

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

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

由于函数返回的是Optional,而不是Integers的Stream,因此无法工作。要解决此问题,您需要更改函数以返回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());

其他选项包括将mapfilter结合使用或使用Optional的方法。请参阅Eran的回答以获取示例。

最新更新