What's the difference between map and flatMap

2019-01-02 18:57发布

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

18条回答
宁负流年不负卿
2楼-- · 2019-01-02 19:34

Stream.flatMap, as it can be guessed by its name, is the combination of a map and a flat operation. 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.

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". Flattening this means transforming it in a "one level" structure : [ 1,2,3,4,5,6,7,8,9 ].

查看更多
妖精总统
3楼-- · 2019-01-02 19:38

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

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.

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

查看更多
琉璃瓶的回忆
4楼-- · 2019-01-02 19:39

Both map and flatMap can be applied to a Stream<T> and they both return a 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.

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.

The flatMap operation 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 flatMap takes a value and returns an array or a List of 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."

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. But of course any stream can be returned.

查看更多
怪性笑人.
5楼-- · 2019-01-02 19:39

flatMap() also takes advantage of partial lazy evaluation of streams. 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?

查看更多
不流泪的眼
6楼-- · 2019-01-02 19:41

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.

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

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/

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.

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]

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

查看更多
临风纵饮
7楼-- · 2019-01-02 19:45

map() in Java 8

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. 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 ? We can use map() to transform the Student Stream into the age stream as below.

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

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

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

flatMap() in Java 8

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. In below example we are using the flatMap to convert the Array of Stream into the String stream.

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

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

查看更多
登录 后发表回答