238

What's the difference between these two methods: Optional.flatMap() and Optional.map()?

An example would be appreciated.

2240
  • 1,547
  • 2
  • 12
  • 30
codependent
  • 23,193
  • 31
  • 166
  • 308
  • 1
    See http://stackoverflow.com/questions/26684562/java-8-map-and-flatmap-methods – Alexis C. Jun 16 '15 at 10:14
  • 7
    @AlexisC. Your link is about Stream's map and flatMap, not Optional. – Eran Jun 16 '15 at 10:16
  • from the java doc: If a value is present, apply the provided Optional-bearing mapping function to it, return that result, otherwise return an empty Optional. This method is similar to map(Function), but the provided mapper is one whose result is already an Optional, and if invoked, flatMap does not wrap it with an additional Optional. – griFlo Jun 16 '15 at 10:16
  • 1
    @Eran That doesn't matter, if you understand how map/flatMap works whether it's for a Stream or not, it's the same for an Optional. If the op understood how it works for a Stream, then he shouldn't asked this question. The concept is the same. – Alexis C. Jun 16 '15 at 10:18
  • 4
    @AlexisC. Not really. Optional's flatMap has little in common with Stream's flatMap. – Eran Jun 16 '15 at 10:22
  • 1
    @Eran I'm speaking about the **conceptual** difference between a map and a flatMap, I'm not making a one-to-one correspondance between `Stream#flatMap` and `Optional#flatMap`. – Alexis C. Jun 16 '15 at 10:28

8 Answers8

230

Use map if the function returns the object you need or flatMap if the function returns an Optional. For example:

public static void main(String[] args) {
  Optional<String> s = Optional.of("input");
  System.out.println(s.map(Test::getOutput));
  System.out.println(s.flatMap(Test::getOutputOpt));
}

static String getOutput(String input) {
  return input == null ? null : "output for " + input;
}

static Optional<String> getOutputOpt(String input) {
  return input == null ? Optional.empty() : Optional.of("output for " + input);
}

Both print statements print the same thing.

muttonUp
  • 6,351
  • 2
  • 42
  • 54
assylias
  • 321,522
  • 82
  • 660
  • 783
  • 6
    Question: would `[flat]Map` ever call the mapping function with an `input == null`? My understanding is that `Optional` sortcuts if it's absent - the [JavaDoc ](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#flatMap-java.util.function.Function-) seems to back this up - "_If a value is present, apply ..._". – Boris the Spider Feb 19 '16 at 10:24
  • 1
    @BoristheSpider Optional.of(null) != Optional.empty() – Diego Martinoia Oct 06 '16 at 09:36
  • 18
    @DiegoMartinoia `Optional.of(null)` is an `Exception`. `Optional.ofNullable(null) == Optional.empty()`. – Boris the Spider Oct 06 '16 at 09:38
  • 1
    @BoristheSpider yes, you are right,. I was trying to reply to your question but I think I made it even more unclear: conceptually, Optional.ofNullable(null) should NOT be empty, but in practice it's considered to be, and therefore map/flatmap do not execute. – Diego Martinoia Oct 06 '16 at 09:44
  • 2
    I think input should never be null in either getOutputOpt or getOutput – DanyalBurke Mar 14 '17 at 11:47
  • Now that I've trained with Optional and streams, I feel like I'm writing cleaner code, thanks for the simple comparison. You can really focus on more important things and just wrapping your checks, parsings and throwing exceptions with one liners :) About Optionals, I suggest you to read this awesome small post : https://leanjava.co/2018/02/01/optional-get-is-code-smell/ – Alex Feb 07 '18 at 10:40
  • Beware! if `s` is empty, then the functions passed to `s.map()` and `s.flatMap()` will not get called. – Josh Grinberg Jan 16 '20 at 20:52
  • Good explanation. – Caio Mar 24 '20 at 17:13
92

They both take a function from the type of the optional to something.

map() applies the function "as is" on the optional you have:

if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));

What happens if your function is a function from T -> Optional<U>?
Your result is now an Optional<Optional<U>>!

That's what flatMap() is about: if your function already returns an Optional, flatMap() is a bit smarter and doesn't double wrap it, returning Optional<U>.

It's the composition of two functional idioms: map and flatten.

catch23
  • 17,519
  • 42
  • 144
  • 217
Diego Martinoia
  • 4,592
  • 1
  • 17
  • 36
16

Okay. You only need to use 'flatMap' when you're facing nested Optionals. Here's the example.

public class Person {

    private Optional<Car> optionalCar;

    public Optional<Car> getOptionalCar() {
        return optionalCar;
    }
}

public class Car {

    private Optional<Insurance> optionalInsurance;

    public Optional<Insurance> getOptionalInsurance() {
        return optionalInsurance;
    }
}

public class Insurance {

    private String name;

    public String getName() {
        return name;
    }

}

public class Test {

    // map cannot deal with nested Optionals
    public Optional<String> getCarInsuranceName(Person person) {
        return person.getOptionalCar()
                .map(Car::getOptionalInsurance) // ① leads to a Optional<Optional<Insurance>
                .map(Insurance::getName);       // ②
    }

}

Like Stream, Optional#map will return a value wrapped by a Optional. That's why we get a nested Optional -- Optional<Optional<Insurance>. And at ②, we want to map it as an Insurance instance, that's how the tragedy happened. The root is nested Optionals. If we can get the core value regardless the shells, we'll get it done. That's what flatMap does.

public Optional<String> getCarInsuranceName(Person person) {
    return person.getOptionalCar()
                 .flatMap(Car::getOptionalInsurance)
                 .map(Insurance::getName);
}

In the end, I stronly recommed the Java 8 In Action to you if you'd like to study Java8 Systematicly.

momonannan
  • 161
  • 1
  • 2
  • This answer needs to explicitly state that the example is pulled from Java 8 in Action, just to make sure this doesn't get flagged as unattributed copying. – Nathan Hughes Aug 24 '22 at 22:07
10

NOTE

Below is the illustration of map() and flatMap() functions, otherwise Optional is primarily designed to be used as a return type only.


As you already may know Optional is a kind of container which may or may not contain a single object, so it can be used wherever you anticipate a null value (you may never see NPE if you use Optional properly).

For example if you have a method which expects a Person object which may be nullable you may want to write the method something like this:

void doSome(Optional<Person> person) {
    /* and here you want to retrieve some property phone out of person
       you may write something like this:
    */
    Optional<String> phone = person.map((p) -> p.getPhone());
    phone.ifPresent((ph) -> dial(ph));
}

class Person {
    private String phone;
    // setters, getters
}

Here you have returned a String type which is automatically wrapped in an Optional type.

If Person class looked like this (i.e. phone is also Optional):

class Person {
    private Optional<String> phone;
    // setters, getters
}

In this case invoking map() function will wrap the returned value in Optional and yield something like:

Optional<Optional<String>>

But you may want Optional<String> instead, so here comes flatMap():

void doSome(Optional<Person> person) {
    Optional<String> phone = person.flatMap((p) -> p.getPhone());
    phone.ifPresent((ph) -> dial(ph));
}

P.S.

Never call get() method (if you need to) on an Optional without checking it with isPresent() unless you can't live without NullPointerException.

informatik01
  • 16,038
  • 10
  • 74
  • 104
SandeepGodara
  • 1,468
  • 16
  • 12
  • 1
    I think this example is likely to distract from the nature of your answer because your class `Person` is misusing `Optional`. It is against the intention of the API to use `Optional` on members like this - see http://mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/003274.html – 8bitjunkie Nov 16 '17 at 14:28
  • @8bitjunkie Thanks for pointing that out, it differs from Scala's Option.. – SandeepGodara Nov 21 '17 at 09:21
7

What helped me was a look at the source code of the two functions.

Map - wraps the result in an Optional.

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
    }
}

flatMap - returns the 'raw' object

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value)); //<---  returns 'raw' object
    }
}
Robert Niestroj
  • 15,299
  • 14
  • 76
  • 119
  • 2
    What do you mean by `flatMap` "returns the 'raw' object"? `flatMap` also returns the mapped object "wrapped" in an `Optional`. The difference is that in the case of `flatMap`, the mapper function wraps the mapped object in the `Optional` while `map` itself wraps the object in `Optional`. – Derek Mahar Oct 09 '19 at 08:46
  • @DerekMahar deleted mine, no need to re-post it, because you've edited your comment right. – maxxyme Oct 10 '19 at 12:04
  • Thank you, that helped me get a nice picture of it! – sebastian89n Jan 26 '23 at 09:57
5
  • Optional.map():

Takes every element and if the value exists, it is passed to the function:

Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);

Now added has one of three values: true or false wrapped into an Optional , if optionalValue was present, or an empty Optional otherwise.

If you don't need to process the result you can simply use ifPresent(), it doesn't have return value:

optionalValue.ifPresent(results::add); 
  • Optional.flatMap():

Works similar to the same method of streams. Flattens out the stream of streams. With the difference that if the value is presented it is applied to function. Otherwise, an empty optional is returned.

You can use it for composing optional value functions calls.

Suppose we have methods:

public static Optional<Double> inverse(Double x) {
    return x == 0 ? Optional.empty() : Optional.of(1 / x);
}

public static Optional<Double> squareRoot(Double x) {
    return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}

Then you can compute the square root of the inverse, like:

Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);

or, if you prefer:

Optional<Double> result = Optional.of(-4.0)
                     .flatMap(MyMath::inverse)
                     .flatMap(MyMath::squareRoot);

If either the inverse() or the squareRoot() returns Optional.empty(), the result is empty.

catch23
  • 17,519
  • 42
  • 144
  • 217
  • 1
    This does not compile. Both of your expressions return an Optional rather than the Double that you are assigning the result to. – JL_SO Jun 05 '19 at 07:32
  • @JL_SO you are right. Because inverse has `Optional` type as return type. – catch23 Jun 05 '19 at 14:42
0

They do the same thing.

The only difference is that, the lambda return's type is wrapped by Optional or not.

For normal usage, map is shorter than flatMap

Example:

package bj;

import java.util.Optional;

import static java.lang.System.out;

public class App {

    public static void main(String[] args) {
        out.println(Optional.of(10).map    (x ->             x * x));
        out.println(Optional.of(10).flatMap(x -> Optional.of(x * x)));
        out.println(Optional.of(10).map    (x -> Optional.of(x * x).get()));

        out.println(Optional.<Integer>empty().map    (x ->             x * x));
        out.println(Optional.<Integer>empty().flatMap(x -> Optional.of(x * x)));
        out.println(Optional.<Integer>empty().map    (x -> Optional.of(x * x).get()));
    }
}

Output:

Optional[100]
Optional[100]
Optional[100]
Optional.empty
Optional.empty
Optional.empty
BaiJiFeiLong
  • 3,716
  • 1
  • 30
  • 28
-1

You can refer below link to understand in detail (best explanation which I could find):

https://www.programmergirl.com/java-8-map-flatmap-difference/

Both map and flatMap - accept Function. The return type of map() is a single value whereas flatMap is returning stream of values

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

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
Mathew Stephen
  • 309
  • 2
  • 7
  • In your method signatures both return types are streams? I thought that `flatMap` would simply unwrap nested optional? Can you explain more how this relates to streams? – Yu Chen Jan 15 '22 at 21:37
  • These are the signatures of Stream::map and Stream::flatMap. While similar in what they do, they are not the same. This answer is mostly irrelevant. – kistlers Sep 21 '22 at 09:28