The Difference Between map() and flatMap()

1. Overview

map() and flatMap() APIs stem from functional languages. In Java 8, we can find them in Optional, Stream and in CompletableFuture (although under a slightly different name).

Streams represent a sequence of objects whereas optionals are classes that represent a value that can be present or absent. Among other aggregate operations, we have the map() and flatMap() methods.

Despite the fact that both have the same return types, they are quite different. Let’s explain these differences by analyzing some examples of streams and optionals.

2. Map and Flatmap in Optionals

The map() method works well with Optional — if the function returns the exact type we need:

Optional<String> s = Optional.of("test");
assertEquals(Optional.of("TEST"), s.map(String::toUpperCase));

However, in more complex cases we might be given a function that returns an Optional too. In such cases using map() would lead to a nested structure, as the map() implementation does an additional wrapping internally.

Let’s see another example to get a better understanding of this situation:

assertEquals(Optional.of(Optional.of("STRING")), 
  Optional
  .of("string")
  .map(s -> Optional.of("STRING")));

As we can see, we end up with the nested structure Optional<Optional<String>>. Although it works, it’s pretty cumbersome to use and does not provide any additional null safety, so it is better to keep a flat structure.

That’s exactly what flatMap() helps us to do:

assertEquals(Optional.of("STRING"), Optional
  .of("string")
  .flatMap(s -> Optional.of("STRING")));

3. Map and Flatmap in Streams

Both methods work similarly for Optional.

The map() method wraps the underlying sequence in a Stream instance, whereas the flatMap() method allows avoiding nested Stream<Stream<R>> structure.

Here, map() produces a Stream consisting of the results of applying the toUpperCase() method to the elements of the input Stream:

List<String> myList = Stream.of("a", "b")
  .map(String::toUpperCase)
  .collect(Collectors.toList());
assertEquals(asList("A", "B"), myList);

map() works pretty well in such a simple case. But what if we have something more complex, such as a list of lists as an input?

Let’s see how it works:

List<List<String>> list = Arrays.asList(
  Arrays.asList("a"),
  Arrays.asList("b"));
System.out.println(list);

This snippet prints a list of lists [[a], [b]].

Now let’s use a flatMap():

System.out.println(list
  .stream()
  .flatMap(Collection::stream)
  .collect(Collectors.toList()));

The result of such a snippet will be flattened to [a, b].

The flatMap() method first flattens the input Stream of Streams to a Stream of Strings. Thereafter, it works similarly to the map() method.

4. Conclusion

Java 8 gives us the opportunity to use the map() and flatMap() methods that originally were used in functional languages.

We can invoke them on Streams and Optionals. These methods help us to get mapped objects by applying the provided mapping function.

As always, the examples in this article are available over on GitHub.

Related posts:

Spring WebClient Requests with Parameters
Java Program to Implement Sorted Circular Doubly Linked List
Java Program to Create a Random Graph Using Random Edge Generation
Giới thiệu Google Guice – Aspect Oriented Programming (AOP)
XML-Based Injection in Spring
JUnit5 @RunWith
HttpClient 4 – Follow Redirects for POST
Generate Spring Boot REST Client with Swagger
Java Program to Implement Quick sort
Từ khóa throw và throws trong Java
Java Program to Implement Segment Tree
Java Program to Convert a Decimal Number to Binary Number using Stacks
Java Program to Implement Solovay Strassen Primality Test Algorithm
Java Program to Implement Variable length array
Java Program to Implement ConcurrentSkipListMap API
Java Program to Implement Adjacency Matrix
Tips for dealing with HTTP-related problems
Assert an Exception is Thrown in JUnit 4 and 5
Spring REST API + OAuth2 + Angular (using the Spring Security OAuth legacy stack)
Beans and Dependency Injection
Java Program to Perform Deletion in a BST
Spring Security 5 for Reactive Applications
Java Program to Find Hamiltonian Cycle in an UnWeighted Graph
A Guide to the ViewResolver in Spring MVC
Java Program to Implement Double Order Traversal of a Binary Tree
Java Program to Implement Graph Coloring Algorithm
Java Program to Test Using DFS Whether a Directed Graph is Strongly Connected or Not
Guava – Join and Split Collections
Java Program to Find Minimum Number of Edges to Cut to make the Graph Disconnected
Why String is Immutable in Java?
Guide to DelayQueue
Spring MVC Content Negotiation