Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

November Discount Launch 2022 – TEMP TOP (NPI)
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

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.

Further reading:

Iterate over a Map in Java

Learn different ways of iterating through the entries of a Map in Java.

Map Serialization and Deserialization with Jackson

A quick and practical guide to serializing and deserializing Java Maps using Jackson.

How to Store Duplicate Keys in a Map in Java?

A quick and practical guide to handling duplicate keys by using multimaps in Java.

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 (for more about flattening, see this article). 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.

November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Generic footer banner
Comments are closed on this article!