Wednesday, September 22, 2021

Difference between map() and flatMap() in Java 8 Stream - Example

The map() and flatmap() are two important operations in the new functional Java 8. Both represent functional operation and they are also methods in java.util.stream.Stream class but the map is used for transformation and flatmap is used for both transformation and flattening, that's why it's called the flatmap. The key difference between map() and flatmap() function is that when you use a map(), it applies a function on each element of the stream and stores the value returned by the function into a new Stream. This way one stream is transformed into another like a Stream of String is transformed into a Stream of Integer where each element is the length of the corresponding Stream.

The key thing to remember is that the function used for transformation in the map() returns a single value. If map() uses a function, which, instead of returning a single value returns a Stream of values then you have a Stream of Stream of values, and flatmap() is used to flat that into a Stream of values.

For example, if we have a Stream of String containing {"12", "34"}, and a method getPermutations() which returns a list of permutations of given String. When you apply that function into each String of Stream using map you will get something like [["12","21"],["34","43"]], but if you use flatmap, you get a Stream of Strings e.g. ["12", "21", "34", "43"].

In this article, we'll see a couple of working examples to understand the difference between map() and flatmap() in Java better. By the way, this is also a popular Java Stream and Lambda question and if you are going for an interview, it's better to revise concepts and understand the difference between map and flatmap better. 

I know it's not easy to understand the map() and flatMap() function, especially if you have not done any functional programming before. I was in the same situation, It took me some time to really understand the purpose of map and flatMap, and thanks to Java SE 8 for Really Impatient book, which helped me ot understand these key functional concepts better.

The explanation given in this book is really great and even if you don't have any functional programming experience, you will understand these new things with a little bit of effort. I highly recommend this book to all Java developers who wish to learn Java 8.

And, If you are serious about improving Java functional programming skills then I highly recommend you check out the Learn Java Functional Programming with Lambdas & Streams by Rang Rao Karnam on Udemy, which explains both Functional Programming and Java Stream fundamentals in good detail





How Stream.map() works in Java 8? Example

The Stream.map() function performs map functional operation i.e. it takes a Stream and transforms it to another Stream. It applies a function on each element of Stream and stores return value into new Stream. 

This way you can transform a Stream of String into a Stream of Integer where Integer could be the length of String if you supply the length() function. This is a very powerful function that is very helpful while dealing with collection in Java.

Here is an example of Stream.map() in Java 8:

List listOfIntegers = Stream.of("1", "2", "3", "4")
               .map(Integer::valueOf)
               .collect(Collectors.toList());

In this example, we have a Stream of String values which represent numbers, by using the map() function we have converted this Stream to Stream of Integers. How? by applying Integer.valueOf() on each element of Stream. That's how "1" converted to integer 1 and so on. Once the transformation is done, we have collected the result into a List by converting Stream to List using Collectors.




How Stream.flatMap() works in Java 8 - Example

The Stream.flatMap() function, as the name suggests, is the combination of a map and a flat operation. This means you first apply the map function and then flattens the result. The key difference is the function used by map operation returns a Stream of values or a list of values rather than a single value, that's why we need flattening. When you flat a Stream of Stream, it gets converted into Stream of values.

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". It's basically a big List containing three more List.  Flattening this means transforming it in a "one level" structure e.g. [ 1,2,3,4,5,6,7,8,9 ] i.e. just one list.

In short,
Before flattening - Stream of List of Integer
After flattening - Stream of Integer


Here is a code example to understand the flatMap() function better:

List evens = Arrays.asList(2, 4, 6);
List odds = Arrays.asList(3, 5, 7);
List primes = Arrays.asList(2, 3, 5, 7, 11);
       
List numbers = Stream.of(evens, odds, primes)
               .flatMap(list -> list.stream())
               .collect(Collectors.toList());
       
System.out.println("flattend list: " + numbers);

Output:
flattend list: [2, 4, 6, 3, 5, 7, 2, 3, 5, 7, 11]

You can see that we have three lists that are merged into one by using a flatMap() function. For mapping, you can see we have used a list.stream() function which returns multiple values instead of a single value. Finally, we have collected the flattened stream into a list. If you want, you can print the final list using the forEach() method.




Stream.map() vs Stream.flatMap() in Java 8

In short, here are the key difference between map() vs flatMap() in Java 8:
  • The function you pass to the map() operation returns a single value.
  • The function you pass to flatMap() operation returns a Stream of value.
  • flatMap() is a combination of map and flat operation. 
  • map() is used for transformation only, but flatMap() is used for both transformation and flattening. 

Now let's see a sample Java program to understand the difference between flatMap() and map() better.

Difference between Map vs FlatMap in Java 8




Java Program to show the difference between map vs flatMap

Here is our sample Java program to demonstrate the real difference between the map() and the flatMap() function of the Stream class in Java 8. As I told you before, map() is used to transform one Stream into another by applying a function on each element, and flatMap() does both transformations as well as flattening.

The flatMap() function can take a Stream of List and return a Stream of values combined from all those lists. In the example below, we have collected the result in a List but you can also print them using the forEach() method of Java 8.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Java Program to demonstrate difference between map()
 * vs flatMap() function in Java 8. Both are defined
 * in Stream class. 
 *
 * @author WINDOWS 8
 */
public class Java8Demo {

    public static void main(String args[]) {

        // foods which helps in weight loss
        List<String> loseWeight = new ArrayList<>();
        loseWeight.add("avocados");
        loseWeight.add("beans");
        loseWeight.add("salad");
        loseWeight.add("oats");
        loseWeight.add("broccoli");
                
        System.out.println("list of String : " + loseWeight);
        
        // let's use map() method to convert list of weight
        // lose food, which are String to list of ints
        // which are length of each food String
        
        List listOfInts = loseWeight.stream()
                .map(s -> s.length())
                .collect(Collectors.toList());
        
        System.out.println("list of ints generate by map(): " + listOfInts);

        
        // flatMap() example, let's first creat a list of list
        List<List> listOfListOfNumber = new ArrayList<>();
        listOfListOfNumber.add(Arrays.asList(2, 4));
        listOfListOfNumber.add(Arrays.asList(3, 9));
        listOfListOfNumber.add(Arrays.asList(4, 16));
        
        System.out.println("list of list : " + listOfListOfNumber);
        
        // let's use flatMap() to flatten this list into
        // list of integers i.e. 2,4,3,9,4,16
        
        List listOfIntegers = listOfListOfNumber.stream()
                .flatMap( list -> list.stream())
                .collect(Collectors.toList());
        
        System.out.println("list of numbers generated by flatMap : " 
                                      + listOfIntegers);
                

    }

}

Output
list of String : [avocados, beans, salad, oats, broccoli]
list of ints generate by map(): [8, 5, 5, 4, 8]
list of list : [[2, 4], [3, 9], [4, 16]]
list of numbers generated by flatMap : [2, 4, 3, 9, 4, 16]

You can see that in the first example, the function used by the map() method returns a single value, the length of the string passed to it, while in the case of flatMap() the method returns a stream, which is basically your multiple values.

That's all about the difference between map() and flatMap() in Java 8. You should use a map() if you just want to transform one Stream into another where each element gets converted to one single value. 

Use flatMap() if the function used by map operation returns multiple values and you want just one list containing all values. If you are still confused between map() vs flatMap() then go read Java SE 8 for Really Impatient By Cay S. Horstmann, one of the best books to learn about new features of Java 8.



Related Java 8 Tutorials
If you are interested in learning more about the new features of Java 8, here are my earlier articles covering some of the important concepts of Java 8:
  • 20 Examples of Date and Time in Java 8 (tutorial)
  • How to use Stream class in Java 8 (tutorial)
  • How to use filter() method in Java 8 (tutorial)
  • How to use forEach() method in Java 8 (example)
  • How to join String in Java 8 (example)
  • How to convert List to Map in Java 8 (solution)
  • How to use peek() method in Java 8 (example)
  • 5 Books to Learn Java 8 from Scratch (books)
  • How to convert the stream to array in Java 8 (tutorial)
  • Java 8 Certification FAQ (guide)
  • Java 8 Mock Exams and Practice Test (test)
  • My favorite courses to learn Stream and Lambda (courses)

Thanks for reading this article so far. If you like this article and difference between map and flatmap in Java Stream API then please share it with your friends and colleagues. If you have any questions, doubts, or feedback then please drop a comment and I'll try to answer your question.

2 comments :

Anonymous said...

Hello there!

When I try to run

List listOfIntegers = listOfListOfNumber.stream() .flatMap( list -> list.stream()) .collect(Collectors.toList());

this error occurs:
Type mismatch: cannot convert from Object to List

Anonymous said...

Instead of
List listOfListOfNumber = new ArrayList<>();

use
List> listOfListOfNumber = new ArrayList<>();

Post a Comment