在Java 8中,Stream.map()和Stream.flatMap()方法之间有什么区别?


当前回答

我不太确定我是否应该回答这个问题,但每当我面对不理解这一点的人时,我就用同样的例子。

假设你有一个苹果。例如,地图是将苹果转换为苹果汁或一对一映射。

同样的苹果,只得到种子,这就是flatMap所做的,或者一对多,一个苹果作为输入,许多种子作为输出。

其他回答

流操作flatMap和map接受函数作为输入。

flatMap期望该函数为流的每个元素返回一个新的流,并返回一个流,该流结合了该函数为每个元素返回的流的所有元素。换句话说,使用flatMap,对于来自源的每个元素,函数将创建多个元素。http://www.zoftino.com/java-stream-examples#flatmap-operation

Map期望函数返回一个转换后的值,并返回一个包含转换后元素的新流。换句话说,使用map,对于来自源的每个元素,函数将创建一个转换后的元素。 http://www.zoftino.com/java-stream-examples#map-operation

flatMap()还利用了流的部分延迟求值。它将读取第一个流,只有在需要时才会进入下一个流。这里详细解释了这种行为:flatMap保证是懒惰的吗?

我想举两个例子来说明更实际的观点: 第一个使用地图的例子:

@Test
public void convertStringToUpperCaseStreams() {
    List<String> collected = Stream.of("a", "b", "hello") // Stream of String 
            .map(String::toUpperCase) // Returns a stream consisting of the results of applying the given function to the elements of this stream.
            .collect(Collectors.toList());
    assertEquals(asList("A", "B", "HELLO"), collected);
}

在第一个例子中没有什么特别的,一个函数被应用来返回大写的String。

第二个使用flatMap的例子:

@Test
public void testflatMap() throws Exception {
    List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)) // Stream of List<Integer>
            .flatMap(List::stream)
            .map(integer -> integer + 1)
            .collect(Collectors.toList());
    assertEquals(asList(2, 3, 4, 5), together);
}

在第二个例子中,传递了一个List流。它不是一个整数流! 如果必须使用转换函数(通过map),则首先必须将流平展为其他类型的流(整数流)。 如果flatMap被移除,则返回以下错误:对于参数类型List, int,操作符+未定义。 不可能在整数列表上应用+ 1 !

这对初学者来说是很困惑的。基本的区别是map为列表中的每个条目发出一个项,而flatMap基本上是一个map + flatten操作。更清楚地说,当你需要多个值时使用flatMap,例如当你期望一个循环返回数组时,flatMap在这种情况下非常有用。

我写了一篇关于这方面的博客,你可以在这里查看。

流。flatMap,顾名思义,是映射和平面操作的组合。这意味着您首先将一个函数应用到元素上,然后将其压平。流。Map仅将函数应用于流,而不会将流平展。

为了理解流的扁平化是由什么组成的,考虑一个像[[1,2,3],[4,5,6],[7,8,9]]这样的结构,它有“两个层次”。扁平化意味着将其转换为“一级”结构:[1,2,3,4,5,6,7,8,9]。