Java 8 中的 map() 和 flatMap() 方法有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26684562/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 03:03:39  来源:igfitidea点击:

What's the difference between map() and flatMap() methods in Java 8?

javajava-8java-stream

提问by cassiomolin

In Java 8, what's the difference between Stream.map()and Stream.flatMap()methods?

在 Java 8 中,Stream.map()Stream.flatMap()方法之间有什么区别?

采纳答案by Stuart Marks

Both mapand flatMapcan be applied to a Stream<T>and they both return a Stream<R>. The difference is that the mapoperation produces one output value for each input value, whereas the flatMapoperation produces an arbitrary number (zero or more) values for each input value.

双方mapflatMap可以应用到Stream<T>他们都回报Stream<R>。不同之处在于该map操作为每个输入值生成一个输出值,而该flatMap操作为每个输入值生成一个任意数量(零个或多个)的值。

This is reflected in the arguments to each operation.

这反映在每个操作的参数中。

The mapoperation takes a Function, which is called for each value in the input stream and produces one result value, which is sent to the output stream.

map操作采用Function,为输入流中的每个值调用它并产生一个结果值,该值被发送到输出流。

The flatMapoperation 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 flatMaptakes a value and returns an array or a Listof 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操作采用一个函数,该函数在概念上希望消耗一个值并生成任意数量的值。但是,在 Java 中,方法返回任意数量的值很麻烦,因为方法只能返回零或一个值。可以想象一个 API,其中的映射器函数flatMap接受一个值并返回一个数组或一个List值,然后将其发送到输出。鉴于这是流库,表示任意数量返回值的一种特别合适的方式是让映射器函数本身返回流!映射器返回的流中的值从流中排出并传递到输出流。每次调用映射器函数返回的值的“块”在输出流中根本没有区别,因此输出被称为“扁平化”。

Typical use is for the mapper function of flatMapto return Stream.empty()if it wants to send zero values, or something like Stream.of(a, b, c)if it wants to return several values. But of course any stream can be returned.

典型的应用是的映射功能flatMap,以回报Stream.empty(),如果要发送零个值,或者类似的东西Stream.of(a, b, c),如果要返回几个值。但是当然可以返回任何流。

回答by Dici

Stream.flatMap, as it can be guessed by its name, is the combination of a mapand a flatoperation. That means that you first apply a function to your elements, and then flatten it. Stream.maponly applies a function to the stream without flattening the stream.

Stream.flatMap顾名思义,就是 amap和 aflat运算的组合。这意味着您首先将函数应用于元素,然后将其展平。Stream.map仅将函数应用于流而不展平流。

To understand what flatteninga stream consists in, consider a structure like [ [1,2,3],[4,5,6],[7,8,9] ]which has "two levels". Flattening this means transforming it in a "one level" structure : [ 1,2,3,4,5,6,7,8,9 ].

要了解扁平化流包含什么,请考虑[ [1,2,3],[4,5,6],[7,8,9] ]具有“两个级别”的结构。扁平化这意味着将其转换为“一级”结构:[ 1,2,3,4,5,6,7,8,9 ]

回答by Philipp

The function you pass to stream.maphas to return one object. That means each object in the input stream results in exactly one object in the output stream.

您传递给的函数stream.map必须返回一个对象。这意味着输入流中的每个对象都会在输出流中产生一个对象。

The function you pass to stream.flatMapreturns a stream for each object. That means the function can return any number of objects for each input object (including none). The resulting streams are then concatenated to one output stream.

您传递给的函数stream.flatMap为每个对象返回一个流。这意味着该函数可以为每个输入对象返回任意数量的对象(包括无)。然后将生成的流连接到一个输出流。

回答by Rudy Vissers

I would like to give 2 examples to get a morepractical point of view:
First example making usage of 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);
}   

Nothing special in the first example, a Functionis applied to return the Stringin uppercase.

在第一个示例中没有什么特别之处, aFunction用于返回String大写。

Second example making usage of flatMap:

第二个例子使用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);
}

In the second example, a Stream of List is passed. It is NOT a Stream of Integer!
If a transformation Function has to be used (through map), then first the Stream has to be flattened to something else (a Stream of Integer).
If flatMap is removed then the following error is returned: The operator + is undefined for the argument type(s) List, int.
It is NOT possible to apply + 1 on a List of Integers!

在第二个示例中,传递了一个列表流。它不是整数流!
如果必须使用转换函数(通过映射),那么首先必须将 Stream 展平为其他内容(整数 Stream)。
如果删除 flatMap,则返回以下错误:运算符 + 未定义参数类型 List, int。
不可能在整数列表上应用 + 1!

回答by TechDog

Please go through the post fully to get a clear idea,

请仔细阅读帖子以获得清晰的想法,

map vs flatMap:

地图与平面地图:

To return a length of each word from a list, we would do something like below..

要从列表中返回每个单词的长度,我们将执行如下操作。

Short Version given below

下面给出的简短版本

When we collect two lists, given below

当我们收集两个列表时,如下所示

Withoutflat map=> [1,2],[1,1]=> [[1,2],[1,1]]Here two lists are placed inside a list, so the output will be list containing lists

没有平面地图=>[1,2],[1,1]=>[[1,2],[1,1]]这里两个列表放在一个列表中,所以输出将是包含列表的列表

Withflat map=> [1,2],[1,1]=> [1,2,1,1]Here two lists are flattened and only the values are placed in list, so the output will be list containing only elements

使用flat map=>[1,2],[1,1]=>[1,2,1,1]这里两个列表被扁平化,只有值放在列表中,所以输出将是只包含元素的列表

Basically it merges all the objects in to one

基本上它将所有对象合并为一个

## Detailed Version has been given below:-

##详细版本如下:-

For example:-
Consider a list [“STACK”, ”OOOVVVER”]and we are trying to return a list like [“STACKOVER”](returning only unique letters from that list) Initially, we would do something like below to return a list [“STACKOVER”]from [“STACK”, ”OOOVVVER”]

例如:-
考虑一个列表[“STACK”, ”OOOVVVER”]并且我们试图返回一个类似[“STACKOVER”]的列表(仅返回该列表中的唯一字母)最初,我们将执行如下操作以返回一个列表 [“STACKOVER”]来自 [“STACK”, “OOOVVVER”]

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());
  }
}

Here the issue is, Lambda passed to the map method returns a String array for each word, So the stream returned by the map method is actually of type Stream, But what we need is Stream to represent a stream of characters, below image illustrates the problem.

这里的问题是,传递给map方法的Lambda为每个单词返回一个String数组,所以map方法返回的流实际上是Stream类型,但是我们需要的是Stream来表示字符流,下图说明了问题。

Figure A:

图一:

enter image description here

在此处输入图片说明

You might think that, We can resolve this problem using flatmap,
OK, let us see how to solve this by using mapand Arrays.streamFirst of all you gonna need a stream of characters instead of a stream of arrays. There is a method called Arrays.stream() that would take an array and produces a stream, for example:

您可能会认为,我们可以使用 flatmap 来解决这个问题,
好吧,让我们看看如何使用mapArrays.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());

The above still does not work, because we now end up with a list of streams (more precisely, Stream>), Instead, we must first convert each word into an array of individual letters and then make each array into a separate stream

上面的还是不行,因为我们现在得到了一个流列表(更准确地说,Stream>),相反,我们必须首先将每个单词转换成一个由单个字母组成的数组,然后将每个数组变成一个单独的流

By using flatMap we should be able to fix this problem as below:

通过使用 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 would perform mapping each array not with stream but with the contents of that stream. All of the individual streams that would get generated while using map(Arrays::stream) get merged into a single stream. Figure B illustrates the effect of using the flatMap method. Compare it with what map does in figure A. Figure Benter image description here

flatMap 将执行映射每个数组,而不是使用流,而是使用该流的内容。使用 map(Arrays::stream) 生成的所有单个流都合并到一个流中。图 B 说明了使用 flatMap 方法的效果。将其与图 A 中的地图进行比较。 图 B在此处输入图片说明

The flatMap method lets you replace each value of a stream with another stream and then joins all the generated streams into a single stream.

flatMap 方法允许您用另一个流替换流的每个值,然后将所有生成的流连接到一个流中。

回答by lalitbhagtani

Map:- This method takes one Function as an argument and returns a new stream consisting of the results generated by applying the passed function to all the elements of the stream.

Map:- 该方法将一个函数作为参数,并返回一个新的流,该流由通过将传递的函数应用于流的所有元素而生成的结果组成。

Let's imagine, I have a list of integer values ( 1,2,3,4,5 ) and one function interface whose logic is square of the passed integer. ( e -> e * e ).

让我们想象一下,我有一个整数值列表( 1,2,3,4,5 )和一个函数接口,其逻辑是传递的整数的平方。( e -> e * e )。

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> newList = intList.stream().map( e -> e * e ).collect(Collectors.toList());

System.out.println(newList);

output:-

输出:-

[1, 4, 9, 16, 25]

As you can see, an output is a new stream whose values are square of values of the input stream.

如您所见,输出是一个新流,其值是输入流值的平方。

[1, 2, 3, 4, 5] -> apply e -> e * e -> [ 1*1, 2*2, 3*3, 4*4, 5*5 ] -> [1, 4, 9, 16, 25 ]

http://codedestine.com/java-8-stream-map-method/

http://codedestine.com/java-8-stream-map-method/

FlatMap :- This method takes one Function as an argument, this function accepts one parameter T as an input argument and returns one stream of parameter R as a return value. When this function is applied to each element of this stream, it produces a stream of new values. All the elements of these new streams generated by each element are then copied to a new stream, which will be a return value of this method.

FlatMap :- 该方法将一个函数作为参数,该函数接受一个参数 T 作为输入参数,并返回一个参数流作为返回值。当这个函数应用于这个流的每个元素时,它会产生一个新值流。然后将每个元素生成的这些新流的所有元素复制到一个新流中,这将是此方法的返回值。

Let's image, I have a list of student objects, where each student can opt for multiple subjects.

让我们想象一下,我有一个学生对象列表,每个学生可以选择多个科目。

List<Student> studentList = new ArrayList<Student>();

  studentList.add(new Student("Robert","5st grade", Arrays.asList(new String[]{"history","math","geography"})));
  studentList.add(new Student("Martin","8st grade", Arrays.asList(new String[]{"economics","biology"})));
  studentList.add(new Student("Robert","9st grade", Arrays.asList(new String[]{"science","math"})));

  Set<Student> courses = studentList.stream().flatMap( e -> e.getCourse().stream()).collect(Collectors.toSet());

  System.out.println(courses);

output:-

输出:-

[economics, biology, geography, science, history, math]

As you can see, an output is a new stream whose values are a collection of all the elements of the streams return by each element of the input stream.

如您所见,输出是一个新流,其值是由输入流的每个元素返回的流的所有元素的集合。

[ S1 , S2 , S3 ] -> [ {"history","math","geography"}, {"economics","biology"}, {"science","math"} ] -> take unique subjects -> [economics, biology, geography, science, history, math]

[ S1 , S2 , S3 ] -> [ {"history","math","geography"}, {"economics","biology"}, {"science","math"} ] -> 选择独特的科目 - > [经济学、生物学、地理、科学、历史、数学]

http://codedestine.com/java-8-stream-flatmap-method/

http://codedestine.com/java-8-stream-flatmap-method/

回答by Rusty Core

Oracle's article on Optional highlights this difference between map and 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:

不幸的是,这段代码不能编译。为什么?变量 computer 的类型是Optional<Computer>,因此调用 map 方法是完全正确的。但是,getSoundcard() 返回一个 Optional 类型的对象。这意味着映射操作的结果是一个类型为 的对象Optional<Optional<Soundcard>>。因此,对 getUSB() 的调用无效,因为最外层的 Optional 包含另一个 Optional 作为其值,这当然不支持 getUSB() 方法。

对于流, flatMap 方法接受一个函数作为参数,它返回另一个流。此函数应用于流的每个元素,这将产生流的流。但是, flatMap 具有将每个生成的流替换为该流的内容的效果。换句话说,该函数生成的所有独立流都合并或“扁平化”为一个流。我们在这里想要的是类似的东西,但我们想将一个两级的 Optional “扁平化”为一个

Optional 还支持 flatMap 方法。它的目的是将转换函数应用于 Optional 的值(就像 map 操作一样),然后将生成的两级 Optional 扁平化为单个 Optional

因此,为了使我们的代码正确,我们需要使用 flatMap 将其重写如下:

String version = computer.flatMap(Computer::getSoundcard)
                   .flatMap(Soundcard::getUSB)
                   .map(USB::getVersion)
                   .orElse("UNKNOWN");

The first flatMap ensures that an Optional<Soundcard>is returned instead of an Optional<Optional<Soundcard>>, and the second flatMap achieves the same purpose to return an Optional<USB>. Note that the third call just needs to be a map() because getVersion() returns a String rather than an Optional object.

第一个 flatMap 确保Optional<Soundcard>返回an而不是 an Optional<Optional<Soundcard>>,第二个 flatMap 实现相同的目的返回 an Optional<USB>。请注意,第三个调用只需要是一个 map() 因为 getVersion() 返回一个 String 而不是一个 Optional 对象。

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

回答by Arsenius

Also good analogy can be with C# if you familiar with. Basically C# Selectsimilar to java mapand C# SelectManyjava flatMap. Same applies to Kotlin for collections.

如果您熟悉 C#,也可以很好地类比。基本上 C#Select类似于 javamap和 C# SelectManyjava flatMap。同样适用于 Kotlin 的集合。

回答by Grzegorz Piwowarek

I have a feeling that most answers here overcomplicate the simple problem. If you already understand how the mapworks that should be fairly easy to grasp.

我有一种感觉,这里的大多数答案都使简单的问题过于复杂。如果您已经了解map工作原理那应该相当容易掌握。

There are cases where we can end up with unwanted nested structures when using map(), the flatMap()method is designed to overcome this by avoiding wrapping.

在某些情况下,我们可能会在使用 时最终得到不需要的嵌套结构map(),该flatMap()方法旨在通过避免包装来克服这一点。



Examples:

例子:

1

1

List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .collect(Collectors.toList());

We can avoid having nested lists by using flatMap:

我们可以避免使用嵌套列表flatMap

List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .flatMap(i -> i.stream())
  .collect(Collectors.toList());

2

2

Optional<Optional<String>> result = Optional.of(42)
      .map(id -> findById(id));

Optional<String> result = Optional.of(42)
      .flatMap(id -> findById(id));

where:

在哪里:

private Optional<String> findById(Integer id)

回答by Bachiri Taoufiq Abderrahman

for a Map we have a list of elements and a (function,action) f so :

对于 Map 我们有一个元素列表和一个 (function,action) f 所以:

[a,b,c] f(x) => [f(a),f(b),f(c)]

and for the flat map we have a list of elements list and we have a (function,action) f and we want the result to be flattened :

对于平面地图,我们有一个元素列表,我们有一个 (function,action) f,我们希望结果被展平:

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]