在Java 8中,Stream.map()和Stream.flatMap()方法之间有什么区别?
当前回答
flatMap()还利用了流的部分延迟求值。它将读取第一个流,只有在需要时才会进入下一个流。这里详细解释了这种行为:flatMap保证是懒惰的吗?
其他回答
这对初学者来说是很困惑的。基本的区别是map为列表中的每个条目发出一个项,而flatMap基本上是一个map + flatten操作。更清楚地说,当你需要多个值时使用flatMap,例如当你期望一个循环返回数组时,flatMap在这种情况下非常有用。
我写了一篇关于这方面的博客,你可以在这里查看。
通过阅读所有的信息,简单的理解方法是:
如果你有一个元素的平面列表,请使用map: [0,1,2,3,4,5] 如果你有一个元素的列表,请使用flatMap:[[1,3,5],[2,4,6]]。这意味着,在映射操作应用于每个元素之前,您的列表需要被平铺
一行回答: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()是一个迭代(一级for循环),那么flatmap()是一个两级迭代(类似于嵌套的for循环)。(输入每个迭代元素foo,然后执行foo. getbarlist()并再次迭代该barList)
Map():获取一个流,对每个元素做一些事情,收集每个进程的单个结果,输出另一个流。“做一些函数”的定义是隐含的。如果任何元素的处理结果为null,则使用null来组成最终流。因此,结果流中的元素数量将等于输入流的数量。
Flatmap():获取元素/流流和函数(显式定义)的流,将函数应用到每个流的每个元素,并将所有中间结果流收集为更大的流(“扁平化”)。如果任何元素的处理结果为null,则为“扁平化”的最后一步提供空流。如果输入是几个流,则结果流中的元素数量是所有输入中所有参与元素的总和。
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()));
推荐文章
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder
- 将JSON字符串转换为HashMap
- web - inf在Java EE web应用程序中用于什么?
- Java 8: Lambda-Streams,过滤方法与异常
- 将JsonNode转换为POJO
- 如何查看IntelliJ中的编译错误列表?
- Java SimpleDateFormat("yyyy-MM-dd' t ' hh:mm:ss' z '")给出的时区为IST
- 杰克逊克服下划线,支持骆驼案
- List、List<?>, List<T>, List<E>, List<Object>
- 在Java中转换字符串到日历对象
- 在java中使用wait()和notify()的简单场景
- 增强的for循环中的Null检查
- c# vs Java Enum(适合c#新手)