Java 8 map() vs flatMap() Methods

Since the inception of Java 8, the Streams API has helped many developers to tackle functional programming within a language that wasn’t so functionally friendly in the past.

Among the many methods available under the new package, perhaps map() and flatMap() hold one of the most controversial comparisons in terms of purpose.

As you may know, a stream is a sequence of objects that can have its members aggregated; that is where the map() and flatMap() methods enter the stage.

These methods are also available for use as Optional objects, as well as to CompletableFuture, although most of the time, a developer will be using them alongside streams.

Let’s take a look at some of the differences between them to understand better the role of each one of them in the Java universe.

Using map() and flatMap() with Optional

Optional left us with a more flexible way to handle optional data primarily being returned by methods. For instance:

Optional myInt = Optional.of(123);

Pretty simple, isn’t it? The map() function allows you to convert an object into something else. In the example above, we do not convert the Optional object itself, but the value it holds within:

myInt.map(i -> i * 2);

The code above will double the value of our current integer.

There are certain times, however, in which we need to manipulate other Optionals within the map() method. Let’s see another example:

Optional myInt = Optional.of(123);
var result = myInt.map(i -> Optional.of(i * 2));

What do you think is going to be the value of the result? You’re right if the answer was an Optional<Optional>. That’s a nested structure of Optionals that’s not so uncommon when programming with Java streams.

If your further implementation requires that the value is represented as a single Optional, then the flatMap() method comes to the rescue:

var result = myInt.flatMap(i -> Optional.of(i * 2)); // single Optional

Using map() and flatMap() with Streams

The usage the map() and flatMap() methods with streams is pretty much the same for Optionals. When you make use of map() with a stream, you will be changing the items of that stream directly, while the flatMap() will underline the nested structure of <Stream<Stream>.

Let’s take a look at an example:

List myList = Stream.of(1, 2, 3)
	.map(i -> i * 2)

You have probably faced that type of code a thousand times before. We are simply doubling the value of every single item of the stream of integers.

Very simple, indeed. However, we must be ready for more complex scenarios in which a nested list structure takes place:

List<List> myList = Arrays.asList(
	Arrays.asList(1, 2, 3),
	Arrays.asList(4, 5, 6));

How can we make sure we’ll be able to double the values of each list without having to use two different iterations? Again, flatMap() is the solution for that:

List result = myList.stream()

This way, the two lists will be flattened to a single one, allowing us to perform the mapping operation to all values instead of just one specific list at a time.


That’s pretty much it! Java 8 streams simplified how you can functionally deal with streams and Optionals via these two methods.

Additionally, make sure to refer to the Java Streams official docs to get more references on what it’s capable of. Good studies!