简体   繁体   English

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

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

在 Java 8 中, Stream.map()Stream.flatMap()方法有什么Stream.flatMap()

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 ,它为输入流中的每个值调用并产生一个结果值,该值被发送到输出流。

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.of(a, b, c)如果要返回多个值,则返回Stream.of(a, b, c)之类的东西。 But of course any stream can be returned.但是当然可以返回任何流。

Stream.flatMap , as it can be guessed by its name, is the combination of a map and a flat operation. Stream.flatMap ,顾名思义,是mapflat操作的结合。 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] ]这样的结构,它具有“两个级别”。 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 ]

I would like to give 2 examples to get a more practical point of view:我想举两个例子以获得实际的观点:
First example making usage of map :使用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 展平为其他内容(整数 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!不可能在整数List上应用 + 1!

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:图一:

在此处输入图片说明

You might think that, We can resolve this problem using flatmap,你可能会认为,我们可以使用 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 方法允许您用另一个流替换流的每个值,然后将所有生成的流连接到一个流中。

One line answer: flatMap helps to flatten a Collection<Collection<T>> into a Collection<T> .一行答案: flatMap有助于将Collection<Collection<T>>扁平化为Collection<T> In the same way, it will also flatten an Optional<Optional<T>> into Optional<T> .同样,它也会将Optional<Optional<T>>扁平化为Optional<T>

在此处输入图片说明

As you can see, with map() only:如您所见,仅使用map()

  • The intermediate type is Stream<List<Item>>中间类型是Stream<List<Item>>
  • The return type is List<List<Item>>返回类型为List<List<Item>>

and with flatMap() :并使用flatMap()

  • The intermediate type is Stream<Item>中间类型是Stream<Item>
  • The return type is List<Item>返回类型为List<Item>

This is the test result from the code used right below:这是下面使用的代码的测试结果

-------- Without flatMap() -------------------------------
     collect() returns: [[Laptop, Phone], [Mouse, Keyboard]]

-------- With flatMap() ----------------------------------
     collect() returns: [Laptop, Phone, Mouse, Keyboard]

Code used :使用的代码

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

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.然后将生成的流连接到一个输出流。

.map is for A -> B mapping .map用于A -> B映射

Stream.of("dog", "cat")              // stream of 2 Strings
    .map(s -> s.length())            // stream of 2 Integers: [3, 3]

it converts any item A to any item B .它将任何项目A转换为任何项目B Javadoc Javadoc


.flatMap is for A -> Stream< B> concatinating .flatMap用于A -> Stream< B>连接

Stream.of("dog", "cat")             // stream of 2 Strings
    .flatMapToInt(s -> s.chars())   // stream of 6 ints:      [d, o, g, c, a, t]

it --1 converts any item A into Stream< B> , then --2 concatenates all the streams into one (flat) stream. it --1 将任何项目A转换为Stream< B> ,然后 --2 将所有流连接成一个(平面)流。 Javadoc Javadoc


Note 1: Although the latter example flats to a stream of primitives (IntStream) instead of a stream of objects (Stream), it still illustrates the idea of the .flatMap .注 1:尽管后一个示例扁平化为基元流 (IntStream) 而不是对象流 (Stream),但它仍然说明了.flatMap的思想。

Note 2: Despite the name, String.chars() method returns ints.注意 2:尽管名称如此,String.chars() 方法返回整数。 So the actual collection will be: [100, 111, 103, 99, 97, 116] , where 100 is the code of 'd' , 111 is the code of 'o' etc. Again, for illustrative purposes, it's presented as [d, o, g, c, a, t].所以实际的集合将是: [100, 111, 103, 99, 97, 116] ,其中100'd'的代码, 111'o'的代码等。同样,为了说明的目的,它表示为[狗猫]。

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)]

I have a feeling that most answers here overcomplicate the simple problem.我有一种感觉,这里的大多数答案都使简单的问题过于复杂。 If you already understand how the map works 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)

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.变量 computer 的类型为Optional<Computer> ,因此调用 map 方法是完全正确的。 However, getSoundcard() returns an object of type Optional.但是,getSoundcard() 返回一个 Optional 类型的对象。 This means the result of the map operation is an object of type Optional<Optional<Soundcard>> .这意味着地图操作的结果是一个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.因此,对 getUSB() 的调用无效,因为最外层的 Optional 包含另一个 Optional 作为其值,这当然不支持 getUSB() 方法。

With streams, the flatMap method takes a function as an argument, which returns another stream.对于流, flatMap 方法接受一个函数作为参数,它返回另一个流。 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.但是, flatMap 具有将每个生成的流替换为该流的内容的效果。 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 “扁平化”为一个

Optional also supports a flatMap method. Optional 还支持 flatMap 方法。 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 .它的目的是将转换函数应用于 Optional 的值(就像 map 操作一样),然后将生成的两级 Optional 扁平化为单个 Optional

So, to make our code correct, we need to rewrite it as follows using flatMap:因此,为了使我们的代码正确,我们需要使用 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> .第一个 flatMap 确保返回Optional<Soundcard>而不是Optional<Optional<Soundcard>> ,第二个 flatMap 实现相同的目的以返回Optional<USB> Note that the third call just needs to be a map() because getVersion() returns a String rather than an Optional object.请注意,第三个调用只需要是一个 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

map() and flatMap() map() 和 flatMap()

  1. map()

Just takes a Function a lambda param where T is element and R the return element built using T. At the end we'll have a Stream with objects of Type R. A simple example can be:只需要一个函数,一个 lambda 参数,其中 T 是元素,R 是使用 T 构建的返回元素。最后,我们将有一个带有类型 R 对象的流。一个简单的例子可以是:

Stream
  .of(1,2,3,4,5)
  .map(myInt -> "preFix_"+myInt)
  .forEach(System.out::println);

It simply takes elements 1 to 5 of Type Integer , uses each element to build a new element from type String with value "prefix_"+integer_value and prints it out.它只需要类型Integer元素 1 到 5,使用每个元素从类型为"prefix_"+integer_value String类型构建一个新元素并将其打印出来。

  1. flatMap()

It is useful to know that flatMap() takes a function F<T, R> where知道 flatMap() 需要一个函数F<T, R>其中很有用

  • T is a type from which a Stream can be built from/with . T 是一种可以从中构建 Stream的类型。 It can be a List (T.stream()), an array (Arrays.stream(someArray)), etc.. anything that from which a Stream can be with/or form.它可以是一个列表 (T.stream())、一个数组 (Arrays.stream(someArray)) 等等。任何可以与/或形成 Stream 的东西。 in the example below each dev has many languages, so dev.在下面的示例中,每个开发人员都有多种语言,因此 dev. Languages is a List and will use a lambda parameter. Languages 是一个列表,将使用 lambda 参数。

  • 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. R 是将使用 T 构建的结果流。知道我们有许多 T 的实例,我们自然会有许多来自 R 的流。所有这些来自 R 类型的流现在将组合成一个来自 R 类型的“平面”流.

Example例子

The examples of Bachiri Taoufiq see its answer here are simple and easy to understanding. Bachiri Taoufiq 的例子见这里的答案,简单易懂。 Just for clarity, let just say we have a team of developers:为了清楚起见,假设我们有一个开发团队:

dev_team = {dev_1,dev_2,dev_3}

, with each developer knowing many languages: ,每个开发人员都知道多种语言:

dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_2 = {lang_e,lang_f}

Applying Stream.map() on dev_team to get the languages of each dev:在 dev_team 上应用Stream.map()以获取每个开发人员的语言:

dev_team.map(dev -> dev.getLanguages())

will give you this structure:会给你这个结构:

{ 
  {lang_a,lang_b,lang_c},
  {lang_d},
  {lang_e,lang_f}
}

which is basically a List<List<Languages>> /Object[Languages[]] .这基本上是一个List<List<Languages>> /Object[Languages[]] Not so very pretty, nor Java8-like!!不太漂亮,也不像Java8!!

with Stream.flatMap() you can 'flatten' things out as it takes the above structure使用Stream.flatMap()您可以“展平”事物,因为它采用上述结构
and turns it into {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f} , which can basically used as List<Languages>/Language[]/etc ...并将其变成{lang_a, lang_b, lang_c, lang_d, lang_e, lang_f} ,基本上可以用作List<Languages>/Language[]/etc ...

so in the end, your code would make more sense like this:所以最后,你的代码会更有意义:

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

or simply:或者干脆:

dev_team
       .stream()    /* {dev_1,dev_2,dev_3} */
       .flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
       .doWhateverWithYourNewStreamHere();

When to use map() and use flatMap() :何时使用 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.当流中的每个 T 类型元素都应该映射/转换为 R 类型的单个元素时,请使用map() 。结果是类型(1 个开始元素 -> 1 个结束元素)和新元素流的映射返回 R 类型。

  • 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) .当流中的每个 T 类型元素都应该映射/转换为 R 类型元素的集合时,请使用flatMap() 。结果是类型(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 .然后这些集合合并(或展)到一个新的 R 类型元素流。这对于表示嵌套循环等很有用。

Pre Java 8: Java 8 之前:

List<Foo> myFoos = new ArrayList<Foo>();
    for(Foo foo: myFoos){
        for(Bar bar:  foo.getMyBars()){
            System.out.println(bar.getMyName());
        }
    }

Post Java 8发布 Java 8

myFoos
    .stream()
    .flatMap(foo -> foo.getMyBars().stream())
    .forEach(bar -> System.out.println(bar.getMyName()));

I am not very sure I am supposed to answer this, but every time I face someone that does not understand this, I use the same example.我不太确定我是否应该回答这个问题,但是每次遇到不理解这一点的人时,我都会使用相同的示例。

Imagine you have an apple.想象一下你有一个苹果。 A map is transforming that apple to apple-juice for example or a one-to-one mapping.例如, map正在将苹果转换为apple-juice一对一映射。

Take that same apple and get only the seeds out of it, that is what flatMap does, or a one to many , one apple as input, many seeds as output.拿同一个苹果,只从中取出种子,这就是flatMap所做的,或者一对多,一个苹果作为输入,许多种子作为输出。

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.让我们想象一下,我有一个整数值列表( 1,2,3,4,5 )和一个函数接口,其逻辑是传递的整数的平方。 ( e -> e * e ). ( 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. FlatMap :- 该方法将一个函数作为参数,该函数接受一个参数 T 作为输入参数,并返回一个参数流作为返回值。 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.然后将每个元素生成的这些新流的所有元素复制到一个新流中,这将是此方法的返回值。

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/

If you think map() as an iteration(one level for loop), flatmap() is a two-level iteration(like a nested for loop).如果你认为map()是一个迭代(一级for循环), flatmap()是一个二级迭代(就像一个嵌套的for循环)。 (Enter each iterated element foo , and do foo.getBarList() and iterate in that barList again) (输入每个迭代元素foo ,然后执行foo.getBarList()并再次在该barList迭代)


map() : take a stream, do something to every element, collect the single result of every process, output another stream. map() :取一个流,对每个元素做一些事情,收集每个进程的单个结果,输出另一个流。 The definition of "do something function" is implicit. “做某事”的定义是隐含的。 If the processment of any element results in null , null is used to compose the final stream.如果任何元素的处理结果为null ,则使用null组成最终流。 So, the number of elements in the resulting stream will be equal to number of input stream.因此,结果流中的元素数量将等于输入流的数量。

flatmap() : take a stream of elements/streams and a function(explicit definition), apply the function to each element of each stream, and collect all the intermediate resulting stream to be a greater stream("flattening"). flatmap() :取一个元素/流流和一个函数(显式定义),将该函数应用于每个流的每个元素,并将所有中间结果流收集为一个更大的流(“扁平化”)。 If the processment of any element results in null , empty stream is provided to the final step of "flattening".如果任何元素的处理结果为null ,则将空流提供给“展平”的最后一步。 The number of elements in the resulting stream, is the total of all participating elements in all inputs, if the input is several streams.如果输入是多个流,则结果流中的元素数是所有输入中所有参与元素的总数。

Simple answer.简单的回答。

The map operation can produce a Stream of Stream .EX Stream<Stream<Integer>> map操作可以产生一个Stream of Stream .EX Stream<Stream<Integer>>

flatMap operation will only produce Stream of something. flatMap操作只会产生Stream的东西。 EX Stream<Integer> EX Stream<Integer>

This is very confusing for beginners.这对初学者来说非常混乱。 The basic difference is map emits one item for each entry in the list and flatMap is basically a map + flatten operation.基本的区别是map为列表中的每个条目发出一个项目,而flatMap基本上是map + flatten操作。 To be more clear, use flatMap when you require more than one value, eg when you are expecting a loop to return arrays, flatMap will be really helpful in this case.更明确地说,当您需要多个值时使用 flatMap,例如,当您期望循环返回数组时, flatMap 在这种情况下将非常有用。

I have written a blog about this, you can check it out here .我写了一篇关于这个的博客,你可以在这里查看

Stream operations flatMap and map accept a function as input.流操作flatMapmap接受一个函数作为输入。

flatMap expects the function to return a new stream for each element of the stream and returns a stream which combines all the elements of the streams returned by the function for each element. flatMap期望函数为流的每个元素返回一个新的流,并返回一个流,该流组合了函数为每个元素返回的流的所有元素。 In other words, with flatMap , for each element from the source, multiple elements will be created by the function.换句话说,使用flatMap ,对于源中的每个元素,函数将创建多个元素。 http://www.zoftino.com/java-stream-examples#flatmap-operation http://www.zoftino.com/java-stream-examples#flatmap-operation

map expects the function to return a transformed value and returns a new stream containing the transformed elements. map期望函数返回一个转换后的值并返回一个包含转换后元素的新流。 In other words, with map , for each element from the source, one transformed element will be created by the function.换句话说,对于map ,对于源中的每个元素,函数将创建一个转换后的元素。 http://www.zoftino.com/java-stream-examples#map-operation http://www.zoftino.com/java-stream-examples#map-operation

Also good analogy can be with C# if you familiar with.如果您熟悉 C#,也可以很好地类比。 Basically C# Select similar to java map and C# SelectMany java flatMap .基本上 C# Select类似于 java map和 C# SelectMany java flatMap Same applies to Kotlin for collections.同样适用于 Kotlin 的集合。

flatMap() also takes advantage of partial lazy evaluation of streams. flatMap()还利用了流的部分惰性求值。 It will read the fist stream and only when required, will go to the next stream.它将读取第一个流,并且仅在需要时才会转到下一个流。 The behaviour is explained in detail here: Is flatMap guaranteed to be lazy?此处详细解释了该行为: 是否保证 flatMap 是惰性的?

This is how I understand it pictorially.这就是我形象地理解它的方式。

Map:地图:

在此处输入图片说明

flat map:平面图:

在此处输入图片说明

By reading all the messages, the simple way to understand is :通过阅读所有消息,简单的理解方法是:

  • use map if you have a flat list of elements: [0, 1, 2, 3, 4, 5]如果您有一个flat的元素列表,请使用map :[0, 1, 2, 3, 4, 5]
  • use flatMap if you have a list of list of elements: [[1, 3, 5], [2, 4, 6]].如果您有元素列表的列表,请使用flatMap :[[1, 3, 5], [2, 4, 6]]。 This means that, your list need to be flattened before the map operation can be applied to each elements这意味着,您的列表需要先展平,然后才能将地图操作应用于每个元素

map() method in java 8 java 8 中的 map() 方法


Java 8 map() operation takes Stream of type T as input and produces a result Stream of type R. It applies given mapper function to each element of input Stream and results are stored in an output Stream. Java 8 map() 操作以 T 类型的 Stream 作为输入并产生 R 类型的结果 Stream。它将给定的映射器函数应用于输入 Stream 的每个元素,并将结果存储在输出 Stream 中。

map() operation produces a single value for each value of input Stream and hence it is also called One-To-One mapping. map() 操作为输入 Stream 的每个值生成一个值,因此它也称为一对一映射。

Let's take an example where we want to return the number of characters for each word in a list.让我们举一个例子,我们想要返回列表中每个单词的字符数。 To do this, we need to apply the function to each element of the list.为此,我们需要将该函数应用于列表的每个元素。 This function job is to accept the word and return the length of the work.这个函数的工作是接受单词并返回工作的长度。

Example of map() map() 示例

public class MapExample {

public static void main(String[] args) {
    List<String> stringList = Arrays.asList("Java Programming", "Java", "Spring Boot", "Java 8", "Hibernate", "Oracle");

    List<Integer> collect = stringList.stream()
            .map(String::length) // :: means method references
            .collect(Collectors.toList());

    collect.forEach(System.out::println);
   }
}

Output::输出::

16
4
11
6
9
6

flatMap() method in java 8 java 8 中的 flatMap() 方法


map() does only mapping, but flatMap() performs mapping as well as flattening. map() 只做映射,但 flatMap() 执行映射和展平。 Flattening means transforming data from Stream<Stream> to Stream.扁平化意味着将数据从 Stream<Stream> 转换为 Stream。 This is the main difference between map() and flatMap().这是 map() 和 flatMap() 之间的主要区别。

flatMap() operation takes Stream T as input and produces a result Stream of type R. It's mapper function produces multiple values for each value of input stream and those multiple values are flattened into a result Stream. flatMap() 操作将 Stream T 作为输入并生成 R 类型的结果流。它的映射器函数为输入流的每个值生成多个值,并将这些多个值展平为结果流。

Let's take an example of flatMap()我们以 flatMap() 为例

public class FlatMapExample {
public static void main(String[] args)
{
    List<List<Integer> > number = new ArrayList<>();
    number.add(Arrays.asList(8, 4));
    number.add(Arrays.asList(43, 23));
    number.add(Arrays.asList(50, 26));
    number.add(Arrays.asList(73, 83));

    System.out.println("List of list or (Arrays of Array) - " + number);

    List<Integer> flatList
            = number.stream()
            .flatMap(list -> list.stream())
            .collect(Collectors.toList());

    System.out.println("List generate by flatMap -"
            + flatList);
    }
  }

Output::输出::

List of list or (Arrays of Array) - [[8, 4], [43, 23], [50, 26], [73, 83]]
List generate by flatMap -[8, 4, 43, 23, 50, 26, 73, 83]

map() Vs.地图()与。 flatMap() methods in java 8 java 8 中的 flatMap() 方法


在此处输入图片说明

map() in Java 8 Java 8中的map()

a stream consisting of the results of applying the given function to the elements of this stream. 由将给定功能应用于此流的元素的结果组成的流。 Map takes an input which describes how the value needs to be transformed into. Map接受一个输入,该输入描述了如何将值转换为值。 Suppose we want to get the age of the Student whose name is Saurabh, till now we have only retrieved the complete object from the stream but how do we do this ? 假设我们想获取名为Saurabh的Student的年龄,到现在为止,我们仅从流中检索了完整的对象,但是我们该如何做呢? We can use map() to transform the Student Stream into the age stream as below. 我们可以使用map()将学生流转换为年龄流,如下所示。

int age = students.stream()
    .filter(student -> SAURABH.equals(student.getName()))
    .map(Student::getAge)
    .findAny()
    .orElse(0);
System.out.printf("*** Age of %s is %d\n",SAURABH, age);

Now lets try to get all the names of the students with the help of collect() 现在让我们尝试在collect()的帮助下获取所有学生的姓名

Set<String> names = students.stream()
       .map(Student::getName) // this will convert the Student Stream into String Stream by 
        // applying the getName()
       .collect(Collectors.toSet());  
System.out.printf("*** All the names from the list is %s\n",names);

map() vs flatMap() map()vs flatMap()

Suppose we want to get all the courses available in students list then we can write the code as below: 假设我们想在学生列表中获得所有课程,那么我们可以编写如下代码:

Set<String> courses = students.stream()
         .map(Student::getCourses)
         .collect(Collectors.toSet())

**Here we will get a compilation error as below **这里我们将得到如下编译错误

Type mismatch: cannot convert from Set to Set To resolve this issue we use flatMap()** 类型不匹配:无法从Set转换为Set为了解决此问题,我们使用flatMap()**

flatMap() in Java 8 Java 8中的flatMap()

It returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. 它返回一个流,该流包括将流中的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容而得到的结果。 flatMap will transform the stream of stream into simple stream. flatMap将把流转换成简单流。 In below example we are using the flatMap to convert the Array of Stream into the String stream. 在下面的示例中,我们使用flatMap将Stream数组转换为String流。

Set<String> courses = students.stream()
         .map(Student::getCourses)
         .flatMap(Arrays::stream)
         .collect(Collectors.toSet());

For more information you can refer to below links : 有关更多信息,您可以参考以下链接:

https://onlyfullstack.blogspot.com/2018/12/map-vs-flatmap-in-java-8.html https://onlyfullstack.blogspot.com/2018/12/map-vs-flatmap-in-java-8.html

http://onlyfullstack.blogspot.com/p/java-8-features.html http://onlyfullstack.blogspot.com/p/java-8-features.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM