map
和flatMap
都可以应用于Stream<T>
并且它们都返回Stream<R>
。区别在于, map
操作为每个输入值生成一个输出值,而flatMap
操作为每个输入值生成任意数量(零个或多个)的值。
这反映在每个操作的参数中。
map
操作采用一个Function
,该函数针对输入流中的每个值调用,并生成一个结果值,该结果值发送至输出流。
flatMap
操作采用的功能在概念上想消耗一个值并产生任意数量的值。但是,在 Java 中,方法返回任意数量的值很麻烦,因为方法只能返回零或一个值。可以想象一个 API,其中flatMap
的映射器函数获取一个值并返回一个数组或List
,然后将其发送到输出。鉴于这是流库,一种表示任意数量的返回值的特别合适的方法是使映射器函数本身返回流!映射器返回的流中的值将从流中排出,并传递到输出流。每次对映射器函数的调用返回的值的 “聚集” 在输出流中根本没有被区分,因此输出被认为是 “扁平化的”。
flatMap
的映射器函数的典型用法是,如果要发送零值,则返回Stream.empty()
Stream.of(a, b, c)
之类的东西。但是,当然可以返回任何流。
Stream.flatMap
map
和flat
操作的组合。这意味着您首先要对元素应用一个函数,然后对其进行展平。 Stream.map
仅将功能应用于流,而不会展平该流。
要了解平化流的含义,请考虑具有 [两个级别[ [1,2,3],[4,5,6],[7,8,9] ]
展平意味着将其转换为 “一级” 结构: [ 1,2,3,4,5,6,7,8,9 ]
。
我想举两个例子以获得更实际的观点:
map
第一个示例:
@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);
}
在第一个示例中没有什么特别的,应用了Function
以大写形式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);
}
在第二个示例中,将传递列表流。它不是整数流!
如果必须使用转换函数(通过映射),则首先必须将 Stream 展平为其他对象(整数流)。
如果flatMap
则会返回以下错误:参数类型 List int 的运算符 + 未定义。
在整数List
中不能应用 + 1!