在Java 8中,Stream.map()和Stream.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
其他回答
map() takes a Stream and transform it to another Stream. It applies a function on each element of Stream and store return value into new Stream. It does not flatten the stream. But flatMap() is the combination of a map and a flat operation i.e, it applies a function to elements as well as flatten them. 2) map() is used for transformation only, but flatMap() is used for both transformation and flattening. please read more here. https://javaint4bytes.blogspot.com/2022/11/stream-flatmap-in-java-with-examples.html
通过阅读所有的信息,简单的理解方法是:
如果你有一个元素的平面列表,请使用map: [0,1,2,3,4,5] 如果你有一个元素的列表,请使用flatMap:[[1,3,5],[2,4,6]]。这意味着,在映射操作应用于每个元素之前,您的列表需要被平铺
map和flatMap都可以应用于一个<T>的流,它们都返回一个<R>的流。不同之处在于map操作为每个输入值生成一个输出值,而flatMap操作为每个输入值生成任意数量(零个或多个)值。
这反映在每个操作的参数中。
map操作接受一个函数,该函数针对输入流中的每个值被调用,并产生一个结果值,该结果值被发送到输出流。
The flatMap operation takes a function that conceptually wants to consume one value and produce an arbitrary number of values. However, in Java, it's cumbersome for a method to return an arbitrary number of values, since methods can return only zero or one value. One could imagine an API where the mapper function for flatMap takes a value and returns an array or a List of values, which are then sent to the output. Given that this is the streams library, a particularly apt way to represent an arbitrary number of return values is for the mapper function itself to return a stream! The values from the stream returned by the mapper are drained from the stream and are passed to the output stream. The "clumps" of values returned by each call to the mapper function are not distinguished at all in the output stream, thus the output is said to have been "flattened."
典型的用法是flatMap的mapper函数返回Stream.empty(),如果它想发送零值,或者类似于Stream。(a, b, c)如果它想返回几个值。当然,任何流都可以返回。
我想举两个例子来说明更实际的观点: 第一个使用地图的例子:
@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 ()
只接受一个函数<T, R>一个lambda参数,其中T是元素,R是使用T构建的返回元素。最后,我们将有一个带有类型为R的对象的流。一个简单的例子可以是:
Stream
.of(1,2,3,4,5)
.map(myInt -> "preFix_"+myInt)
.forEach(System.out::println);
它只是取Type Integer的元素1到5,使用每个元素从String类型中构建一个值为“prefix_”+integer_value的新元素,并打印出来。
flatMap ()
知道flatMap()接受一个函数F<T, R> where是很有用的
T is a type from which a Stream can be built from/with. It can be a List (T.stream()), an array (Arrays.stream(someArray)), etc.. anything that from which a Stream can be with/or form. in the example below each dev has many languages, so dev. Languages is a List and will use a lambda parameter. R is the resulting Stream that will be built using T. Knowing that we have many instances of T, we will naturally have many Streams from R. All these Streams from Type R will now be combined into one single 'flat' Stream from Type R.
例子
Bachiri Taoufiq的例子(见这里的答案)1简单易懂。为了清晰起见,假设我们有一个开发团队:
dev_team = {dev_1,dev_2,dev_3}
,每个开发人员都懂多种语言:
dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_3 = {lang_e,lang_f}
在dev_team上应用Stream.map()来获取每个开发人员的语言:
dev_team.map(dev -> dev.getLanguages())
会给你这样的结构:
{
{lang_a,lang_b,lang_c},
{lang_d},
{lang_e,lang_f}
}
它基本上是一个List<List<Languages>> /Object[Languages[]]。不是很漂亮,也不像java8 !!
使用Stream.flatMap(),你可以“扁平化”的东西,因为它采取上述结构 并将其转换为{lang_a, lang_b, lang_c, lang_d, lang_e, lang_f},基本上可以作为List<Languages>/Language[]/etc…
所以最后,你的代码会像这样更有意义:
dev_team
.stream() /* {dev_1,dev_2,dev_3} */
.map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
.flatMap(languages -> languages.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
.doWhateverWithYourNewStreamHere();
或者仅仅是:
dev_team
.stream() /* {dev_1,dev_2,dev_3} */
.flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
.doWhateverWithYourNewStreamHere();
何时使用map()和flatMap():
Use map() when each element of type T from your stream is supposed to be mapped/transformed to a single element of type R. The result is a mapping of type (1 start element -> 1 end element) and new stream of elements of type R is returned. Use flatMap() when each element of type T from your stream is supposed to mapped/transformed to a Collections of elements of type R. The result is a mapping of type (1 start element -> n end elements). These Collections are then merged (or flattened) to a new stream of elements of type R. This is useful for example to represent nested loops.
Java 8 之前:
List<Foo> myFoos = new ArrayList<Foo>();
for(Foo foo: myFoos){
for(Bar bar: foo.getMyBars()){
System.out.println(bar.getMyName());
}
}
后Java 8
myFoos
.stream()
.flatMap(foo -> foo.getMyBars().stream())
.forEach(bar -> System.out.println(bar.getMyName()));
推荐文章
- Intellij IDEA Java类在保存时不能自动编译
- 何时使用Mockito.verify()?
- 在maven中安装mvn到底做什么
- 不可变与不可修改的集合
- 如何在JSON中使用杰克逊更改字段名
- GSON -日期格式
- 如何从线程捕获异常
- 无法解析主机"<URL here>"没有与主机名关联的地址
- 如何在Java中打印二叉树图?
- String.format()在Java中格式化双重格式
- com.jcraft.jsch.JSchException: UnknownHostKey
- Java中的操作符重载
- 如何加速gwt编译器?
- 在Hibernate中重新连接分离对象的正确方法是什么?
- 应该……接住环内还是环外?