在Java 8中,Stream.map()和Stream.flatMap()方法之间有什么区别?
当前回答
对于Map,我们有一个元素列表和一个(函数,动作)f,这样:
[a,b,c] f(x) => [f(a),f(b),f(c)]
对于平面映射,我们有一个元素列表,我们有一个(function,action) f,我们希望结果是扁平的:
[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]
其他回答
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()));
一行回答:flatMap帮助将Collection<Collection<T>>平铺为Collection<T>。以同样的方式,它还将一个Optional<Optional<T>>压扁为Optional<T>。
如你所见,只使用map():
中间类型为Stream<List<Item>> 返回类型为List<List<Item>>
和flatMap():
中间类型是Stream<Item> 返回类型为List<Item>
这是下面使用的代码的测试结果:
-------- Without flatMap() -------------------------------
collect() returns: [[Laptop, Phone], [Mouse, Keyboard]]
-------- With flatMap() ----------------------------------
collect() returns: [Laptop, Phone, Mouse, Keyboard]
代码使用:
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class Parcel {
String name;
List<String> items;
public Parcel(String name, String... items) {
this.name = name;
this.items = Arrays.asList(items);
}
public List<String> getItems() {
return items;
}
public static void main(String[] args) {
Parcel amazon = new Parcel("amazon", "Laptop", "Phone");
Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard");
List<Parcel> parcels = Arrays.asList(amazon, ebay);
System.out.println("-------- Without flatMap() ---------------------------");
List<List<String>> mapReturn = parcels.stream()
.map(Parcel::getItems)
.collect(Collectors.toList());
System.out.println("\t collect() returns: " + mapReturn);
System.out.println("\n-------- With flatMap() ------------------------------");
List<String> flatMapReturn = parcels.stream()
.map(Parcel::getItems)
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println("\t collect() returns: " + flatMapReturn);
}
}
通过阅读所有的信息,简单的理解方法是:
如果你有一个元素的平面列表,请使用map: [0,1,2,3,4,5] 如果你有一个元素的列表,请使用flatMap:[[1,3,5],[2,4,6]]。这意味着,在映射操作应用于每个元素之前,您的列表需要被平铺
传递给流的函数。Map必须返回一个对象。这意味着输入流中的每个对象都会导致输出流中的一个对象。
传递给流的函数。flatMap为每个对象返回一个流。这意味着该函数可以为每个输入对象返回任意数量的对象(包括none)。然后将结果流连接到一个输出流。
流操作flatMap和map接受函数作为输入。
flatMap期望该函数为流的每个元素返回一个新的流,并返回一个流,该流结合了该函数为每个元素返回的流的所有元素。换句话说,使用flatMap,对于来自源的每个元素,函数将创建多个元素。http://www.zoftino.com/java-stream-examples#flatmap-operation
Map期望函数返回一个转换后的值,并返回一个包含转换后元素的新流。换句话说,使用map,对于来自源的每个元素,函数将创建一个转换后的元素。 http://www.zoftino.com/java-stream-examples#map-operation
推荐文章
- Intellij IDEA Java类在保存时不能自动编译
- 何时使用Mockito.verify()?
- 在maven中安装mvn到底做什么
- 不可变与不可修改的集合
- 如何在JSON中使用杰克逊更改字段名
- GSON -日期格式
- 如何从线程捕获异常
- 无法解析主机"<URL here>"没有与主机名关联的地址
- 如何在Java中打印二叉树图?
- String.format()在Java中格式化双重格式
- com.jcraft.jsch.JSchException: UnknownHostKey
- Java中的操作符重载
- 如何加速gwt编译器?
- 在Hibernate中重新连接分离对象的正确方法是什么?
- 应该……接住环内还是环外?