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

本文翻译自:What's the difference between map() and flatMap() methods in Java 8?

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


#1楼

参考:https://stackoom.com/question/1nxsA/Java-中的map-和flatMap-方法有什么区别


#2楼

Stream.flatMap , as it can be guessed by its name, is the combination of a map and a flat operation. Stream.flatMapStream.flatMapmapflat操作的组合。 That means that you first apply a function to your elements, and then flatten it. 这意味着您首先要对元素应用一个函数,然后对其进行展平。 Stream.map only applies a function to the stream without flattening the stream. Stream.map仅将功能应用于流,而不Stream.map平该流。

To understand what flattening a stream consists in, consider a structure like [ [1,2,3],[4,5,6],[7,8,9] ] which has "two levels". 要了解使流变平的含义,请考虑具有[ [1,2,3],[4,5,6],[7,8,9] ]两个级别[ [1,2,3],[4,5,6],[7,8,9] ]结构。 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 ]


#3楼

The function you pass to stream.map has to return one object. 传递给stream.map的函数必须返回一个对象。 That means each object in the input stream results in exactly one object in the output stream. 这意味着输入流中的每个对象都会在输出流中恰好产生一个对象。

The function you pass to stream.flatMap returns a stream for each object. 传递给stream.flatMap的函数为每个对象返回一个流。 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. 然后将生成的流连接到一个输出流。


#4楼

Both map and flatMap can be applied to a Stream<T> and they both return a Stream<R> . mapflatMap都可以应用于Stream<T>并且它们都返回Stream<R> The difference is that the map operation produces one output value for each input value, whereas the flatMap operation produces an arbitrary number (zero or more) values for each input value. 区别在于, map操作为每个输入值生成一个输出值,而flatMap操作为每个输入值生成任意数量(零个或多个)的值。

This is reflected in the arguments to each operation. 这反映在每个操作的参数中。

The map operation 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 ,该Function针对输入流中的每个值调用,并生成一个结果值,该结果值发送至输出流。

The flatMap operation takes a function that conceptually wants to consume one value and produce an arbitrary number of values. flatMap操作采用的功能在概念上想消耗一个值并产生任意数量的值。 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. 但是,在Java中,方法返回任意数量的值很麻烦,因为方法只能返回零或一个值。 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. 可以想象一个API,其中flatMap的mapper函数获取一个值并返回一个数组或值List ,然后将其发送到输出。 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." 在输出流中根本不会区分每次对映射器函数的调用返回的值的“聚集”,因此可以说输出已被“扁平化”。

Typical use is for the mapper function of flatMap to 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. flatMap的映射器函数的典型用法是,如果要发送零值,则返回Stream.empty()如果要返回Stream.empty()值,则类似Stream.of(a, b, c)之类的东西。 But of course any stream can be returned. 但是,当然可以返回任何流。


#5楼

I would like to give 2 examples to get a more practical 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 Function is applied to return the String in uppercase. 在第一个示例中没有什么特别的,应用了一个Function以大写形式返回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). 如果必须使用转换函数(通过映射),则首先必须将Stream展平为其他对象(整数流)。
If flatMap is removed then the following error is returned: The operator + is undefined for the argument type(s) List, int. 如果删除了flatMap,则返回以下错误: 对于参数类型List int,运算符+未定义。
It is NOT possible to apply + 1 on a List of Integers! 在整数列表中不能应用+ 1!


#6楼

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 当我们收集两个列表时,如下所示

Without flat 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]]这里两个列表放在一个列表内,因此输出将是包含列表的列表

With flat 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 ”]来自[“堆栈”,“ 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: 图A:

在此处输入图片说明

You might think that, We can resolve this problem using flatmap, 您可能会认为,我们可以使用平面图解决此问题,
OK, let us see how to solve this by using map and Arrays.stream First of all you gonna need a stream of characters instead of a stream of arrays. 好的,让我们看看如何使用mapArrays.stream解决这个问题。首先,您需要一个字符流而不是一个数组流。 There is a method called Arrays.stream() that would take an array and produces a stream, for example: 有一个称为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. flatMap将执行每个数组的映射,而不是使用流,而是使用该流的内容。 All of the individual streams that would get generated while using map(Arrays::stream) get merged into a single stream. 使用map(Arrays :: stream)时将生成的所有单个流都合并到单个流中。 Figure B illustrates the effect of using the flatMap method. 图B说明了使用flatMap方法的效果。 Compare it with what map does in figure A. Figure B 将其与图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方法使您可以用另一个流替换一个流的每个值,然后将所有生成的流合并为一个流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值