Core Java

Java 8 map () vs flatMap()


Java 8 map() and flatMap() operations can be applied to either Streams or Optional.

Stream<T> can be thought of as a wrapper holding a sequence of objects of type T to help us operate over them.

Similarly, Optional<T> in Java 8 adds a layer to the top of element T, to help us determine if it exists or not. This helps in reducing null pointer checks to large extents, leading to a cleaner code.

In this tutorial, we’ll look at the usages of the map() and flatMap() for both scenarios and will cover the difference between them.

Java 8 map() vs flatMap():

Java 8 flatMap() can be thought of as:

flatMap = flattening + map

To understand things better, let’s further breakdown our learning:

1. Optional<T>:

As we know map() method accepts a type T1 and transforms it into another type T2:

Optional<String> intStr = Optional.of("23");
Optional<Integer> intValue =;

We have invoked map() operation over Optional<String> by passing in a function returning element of another type T2 i.e. Integer for us. Works well!

But what if the function we have returns an Optional<T> instead of T. How to deal with it?

Let’s see an example of that as well:

//works just fine
Optional<String> intStr = Optional.of("23");
Optional<Optional<Integer>> intValue = -> Optional.of(Integer.parseInt(str));

The above code will work fine for us. But now, we have an Optional wrapper over another Optional wrapper holding our outcome. This additional wrapper serve us no additional benefit so why to add an overhead?

Here the flatMap() method comes to our rescue making it look cleaner for us:

Optional<String> intStr = Optional.of("23");
Optional<Integer> intValue = intStr.flatMap(str -> Optional.of(Integer.parseInt(str)));

All it does here is to flatten out that extra wrapper, we don’t need.


2. Streams

Let’s look at both map() and flatMap() declarations for Streams, as defined in JavaDoc:

<R> Stream<R> map(Function<? super T, ? extends R> mapper)

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

The flatMap() function accepts a mapper function returning a Stream. It helps us avoid dealing with Stream<Stream<T>>, by flattening that structure to Stream<T> along with performing the basic map operation.

Let’s understand it further with an example.

Let’s first consider using a map() with Streams:

List<Integer> listOfIntegers = Arrays.asList("5", "10", "15", "20")

map() operation applies the mapper function to each element in the stream, one after the other. On collecting the results, we have a list of integers.


On the other hand, we might feel the need of using flatMap() when working with a stream of streams. For eg: when opening a stream over a List<List<String>>:

List<String> listOfIntegers = Arrays.asList(Arrays.asList("5", "10"), Arrays.asList("15", "20")) 

The flatMap() operation above flattens the list from [[5, 10], [15, 20]] to [5, 10, 15, 20] using a mapping transformation.

Differences In A Nutshell:

Java 8 map()Java 8 flatMap()
Accepts a function returning a single value.Accepts a function returning stream of values eg:, etc or Optional, when working with Optionals.
Transforms each element from type T to R.flatMap involves flattening and mapping operation.

All streams generated by mapper function later gets flattened to a single stream.


In this tutorial, we looked at the differences between map() and flatMap() operation when working with Java 8 Streams and Optional. By now, we understand the usages of these two methods and the differences between them.


Be the First to comment.

Leave a Comment

Your email address will not be published. Required fields are marked *