在Java 8中,Stream.map()和Stream.flatMap()方法之间有什么区别?
当前回答
我有一种感觉,这里的大多数答案都把简单的问题复杂化了。如果你已经理解了地图是如何工作的,那就很容易掌握了。
在使用map()时,有些情况下我们可能会得到不需要的嵌套结构,flatMap()方法的设计是通过避免换行来克服这一问题。
例子:
1
List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
.collect(Collectors.toList());
我们可以使用flatMap来避免使用嵌套列表:
List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
.flatMap(i -> i.stream())
.collect(Collectors.toList());
2
Optional<Optional<String>> result = Optional.of(42)
.map(id -> findById(id));
Optional<String> result = Optional.of(42)
.flatMap(id -> findById(id));
地点:
private Optional<String> findById(Integer id)
其他回答
流。flatMap,顾名思义,是映射和平面操作的组合。这意味着您首先将一个函数应用到元素上,然后将其压平。流。Map仅将函数应用于流,而不会将流平展。
为了理解流的扁平化是由什么组成的,考虑一个像[[1,2,3],[4,5,6],[7,8,9]]这样的结构,它有“两个层次”。扁平化意味着将其转换为“一级”结构:[1,2,3,4,5,6,7,8,9]。
这对初学者来说是很困惑的。基本的区别是map为列表中的每个条目发出一个项,而flatMap基本上是一个map + flatten操作。更清楚地说,当你需要多个值时使用flatMap,例如当你期望一个循环返回数组时,flatMap在这种情况下非常有用。
我写了一篇关于这方面的博客,你可以在这里查看。
Oracle关于Optional的文章强调了map和flatmap的区别:
String version = computer.map(Computer::getSoundcard)
.map(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
Unfortunately, this code doesn't compile. Why? The variable computer is of type Optional<Computer>, so it is perfectly correct to call the map method. However, getSoundcard() returns an object of type Optional. This means the result of the map operation is an object of type Optional<Optional<Soundcard>>. As a result, the call to getUSB() is invalid because the outermost Optional contains as its value another Optional, which of course doesn't support the getUSB() method. With streams, the flatMap method takes a function as an argument, which returns another stream. This function is applied to each element of a stream, which would result in a stream of streams. However, flatMap has the effect of replacing each generated stream by the contents of that stream. In other words, all the separate streams that are generated by the function get amalgamated or "flattened" into one single stream. What we want here is something similar, but we want to "flatten" a two-level Optional into one. Optional also supports a flatMap method. Its purpose is to apply the transformation function on the value of an Optional (just like the map operation does) and then flatten the resulting two-level Optional into a single one. So, to make our code correct, we need to rewrite it as follows using flatMap:
String version = computer.flatMap(Computer::getSoundcard)
.flatMap(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
第一个flatMap确保返回Optional<Soundcard> 而不是一个Optional<Optional<Soundcard>>,和第二个flatMap 实现相同的目的,返回Optional<USB>。注意 第三个调用只需要一个map(),因为getVersion()返回一个 字符串而不是可选对象。
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
flatMap()还利用了流的部分延迟求值。它将读取第一个流,只有在需要时才会进入下一个流。这里详细解释了这种行为:flatMap保证是懒惰的吗?
请仔细阅读这篇文章以获得一个清晰的概念,
map vs flatMap:
要从列表中返回每个单词的长度,我们将执行如下操作。
简短版本如下
当我们收集两个列表时,如下所示
没有flat map =>[1,2],[1,1] =>[[1,2],[1,1]]这里在一个列表中放置了两个列表,因此输出将是包含列表的列表
使用flat map =>[1,2],[1,1] =>[1,2,1,1],这里两个列表被平铺,只有值被放在列表中,因此输出将是只包含元素的列表
基本上,它将所有对象合并为一个
##详细版本已给出如下:-
例如:-考虑一个列表[" STACK ", " OOOVVVER "],我们试图返回一个列表像[" STACKOVER "](从该列表中只返回唯一的字母) 最初,我们将执行如下操作,从[" STACK ", " OOOVVVER "]返回一个列表[" STACKOVER "]
public class WordMap {
public static void main(String[] args) {
List<String> lst = Arrays.asList("STACK","OOOVER");
lst.stream().map(w->w.split("")).distinct().collect(Collectors.toList());
}
}
这里的问题是,传递给map方法的Lambda为每个单词返回一个字符串数组,因此map方法返回的流实际上是流类型,但我们需要的是流来表示字符流,下面的图像说明了这个问题。
图一:
你可能会想,我们可以用flatmap来解决这个问题,让我们看看如何用map和arrays。stream来解决这个问题 首先,你需要一个字符流而不是数组流。有一个叫做Arrays.stream()的方法,它将接受一个数组并生成一个流,例如:
String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
.map(Arrays::stream).distinct() //Make array in to separate stream
.collect(Collectors.toList());
上面的方法仍然不起作用,因为我们现在得到了一个流的列表(更准确地说,流>)。相反,我们必须首先将每个单词转换为一个单独的字母数组,然后将每个数组转换为一个单独的流
通过使用flatMap,我们应该能够修复这个问题如下:
String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
.flatMap(Arrays::stream).distinct() //flattens each generated stream in to a single stream
.collect(Collectors.toList());
flatMap不是用流而是用流的内容来映射每个数组。在使用map(Arrays::stream)时生成的所有单独流被合并成一个流。图B说明了使用flatMap方法的效果。将其与图A中的map进行比较。 图B
flatMap方法允许您用另一个流替换流的每个值,然后将所有生成的流连接到单个流中。
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder
- 将JSON字符串转换为HashMap