299

I am trying to understand the difference between the Optional<T>.orElse() and Optional<T>.orElseGet() methods.

The description for the orElse() method is "Return the value if present, otherwise return other."

While, the description for the orElseGet() method is "Return the value if present, otherwise invoke other and return the result of that invocation."

The orElseGet() method takes a Supplier functional interface, which essentially does not take any parameters and returns T.

In which situation would you need to use orElseGet()? If you have a method T myDefault() why wouldn't you just do optional.orElse(myDefault()) rather than optional.orElseGet(() -> myDefault()) ?

It does not seem that orElseGet() is postponing the execution of the lambda expression to some later time or something, so what's the point of it? (I would have thought that it would be more useful if it returned a safer Optional<T> whose get() never throws a NoSuchElementException and isPresent() always returns true... but evidently its not, it just returns T like orElse()).

Is there some other difference I am missing?

jbx
  • 21,365
  • 18
  • 90
  • 144
  • 10
    The reason is when you use `orElseGet` it calls supplier only if value is absent. – Alex Salauyou Oct 16 '15 at 12:09
  • 16
    Ah ok got it. So in the case of `orElse()` the `myDefault()` method is still called, but its return value is just not used. – jbx Oct 16 '15 at 12:16
  • 4
    Upvoted question because from what I've seen misunderstanding or simply forgetting to use `orElseGet()` can result in some serious bugs: https://medium.com/alphadev-thoughts/are-you-using-optional-orelse-wrong-4f39585a19da – softarn May 24 '18 at 15:07
  • 2
    A good explanation is found here: https://www.baeldung.com/java-optional-or-else-vs-or-else-get – Nestor Milyaev Jun 09 '20 at 14:29

9 Answers9

246

Short Answer:

  • orElse() will always call the given function whether you want it or not, regardless of Optional.isPresent() value
  • orElseGet() will only call the given function when the Optional.isPresent() == false

In real code, you might want to consider the second approach when the required resource is expensive to get.

// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource()); 

// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource()) 

For more details, consider the following example with this function:

public Optional<String> findMyPhone(int phoneId)

The difference is as below:

                           X : buyNewExpensivePhone() called

+——————————————————————————————————————————————————————————————————+——————————————+
|           Optional.isPresent()                                   | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone())          |   X  |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) |      |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+

When optional.isPresent() == false, there is no difference between two ways. However, when optional.isPresent() == true, orElse() always calls the subsequent function whether you want it or not.

Finally, the test case used is as below:

Result:

------------- Scenario 1 - orElse() --------------------
  1.1. Optional.isPresent() == true (Redundant call)
    Going to a very far store to buy a new expensive phone
    Used phone: MyCheapPhone

  1.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone

------------- Scenario 2 - orElseGet() --------------------
  2.1. Optional.isPresent() == true
    Used phone: MyCheapPhone

  2.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone

Code:

public class TestOptional {
    public Optional<String> findMyPhone(int phoneId) {
        return phoneId == 10
                ? Optional.of("MyCheapPhone")
                : Optional.empty();
    }

    public String buyNewExpensivePhone() {
        System.out.println("\tGoing to a very far store to buy a new expensive phone");
        return "NewExpensivePhone";
    }


    public static void main(String[] args) {
        TestOptional test = new TestOptional();
        String phone;
        System.out.println("------------- Scenario 1 - orElse() --------------------");
        System.out.println("  1.1. Optional.isPresent() == true (Redundant call)");
        phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  1.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("------------- Scenario 2 - orElseGet() --------------------");
        System.out.println("  2.1. Optional.isPresent() == true");
        // Can be written as test::buyNewExpensivePhone
        phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  2.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");
    }
}
Hoa Nguyen
  • 13,452
  • 11
  • 45
  • 44
  • 1
    I think you may have an Error in your picture, it should say "orElseGet" on the right ? Besides that, great example. – Yalla T. Feb 02 '18 at 11:56
  • Yes you are correct. Thank you :) I will update it in next few hours – Hoa Nguyen Feb 02 '18 at 12:35
  • For the second bullet point, seems should be `Optional.isPresent() == false` instead (false, not true) – Manuel Jordan May 31 '18 at 16:50
  • 1
    Great example - but I really don't get how the Javadocs for `Optional.orElse`which states `If a value is present, returns the value, otherwise returns other` can imply this behaviour... – Erik Finnman Nov 23 '18 at 12:44
  • Based on your explanation, for me it looks like that `orElse()` behaves similar to `finally` in `try-catch` expression. Am I correct? – Mike Jan 10 '19 at 09:33
  • Hi Mike B, in that regards I agree with you. Code block within two situations will always be executed in any cases. – Hoa Nguyen Jan 14 '19 at 19:06
  • 1
    @Mike - sure, but it's probably not a very good mnemonic to keep because they are also vastly different concepts. They just _happen_ to be comparable in this specific instance but it's a bit like saying that cloud over there looks like a horse ;) – Oskar Lund Mar 08 '22 at 10:43
233

Take these two scenarios:

Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );

If opt doesn't contain a value, the two are indeed equivalent. But if opt does contain a value, how many Foo objects will be created?

P.s.: of course in this example the difference probably wouldn't be measurable, but if you have to obtain your default value from a remote web service for example, or from a database, it suddenly becomes very important.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • 41
    Thanks for the clarification guys. So the difference is subtle but significant. In the second case, it won't create a new `Foo` object, while in the first case it will create it, but not use it if there is a value inside the `Optional`. – jbx Oct 16 '15 at 12:18
  • 8
    @jbx Yes, and in my noddy example it arguably doesn't make any real difference, but if you have to obtain your default value from a remote web service for example, or from a database, the difference suddenly becomes very important. – biziclop Oct 16 '15 at 12:19
  • Yes of course. Even if it just requires computation and you're doing it a 1000 times or something on some bulk stream, it will definitely make a difference. – jbx Oct 16 '15 at 12:20
  • 1
    @jbx: doing calculations and doing it thousand times are not necessarily making a difference as the JVM is not that bad at removing unused stuff. Nevertheless, it’s not always good to *rely* on it and biziclop has already provided better examples. Fetching data from an external source is not only expensive but also cannot optimized away due to the observable side-effects. – Holger Oct 16 '15 at 13:56
  • @Holger Mmm... I don't think the JVM will optimize to that level. You could simply have a `System.out.println()` in the `myDefault()` method. It will definitely call `myDefault()` a thousand times, because it needs to evaluate it to get the return value, even if it might be the same as the previous one... and the `println()` will be called a thousand times. It can't suddenly decide not to evaluate a call passed as an argument to another method, just because its called `orElse()`. – jbx Oct 16 '15 at 14:01
  • 2
    @jbx: you are mixing two things up. There are already questions on SO regarding strange benchmark results which simply were cause by not using the result of a computation. The JVM *can* do that. On the other hand, `System.out.println()` is *not* a calculation but a statement producing an observable side-effect. And I already said that observable side-effects will hinder optimizations (the console output stream *is* an external resource). – Holger Oct 16 '15 at 14:06
  • 1
    @jbx `It can't suddenly decide not to evaluate a call passed as an argument to another method, just because its called orElse()` You're right, it can't do that (yet), but what it can do is inline method calls into the calling code, and then come to the conclusion that parts of the code do something that cannot be observed from the outside. – biziclop Oct 16 '15 at 15:07
  • 23
    That's the first time I see a question instead of an answer been accepted. – Kirill G. Jan 26 '18 at 05:07
  • 1
    @KirillG. the world is truly going haywire isn't it ;) – jbx Mar 04 '18 at 21:16
  • 6
    "*if you have to obtain your default value from a remote web service for example*" this was my scenario exactly. In my case, the optional was a query, and the default in the absence of a query was to fetch all values...yeah, orElseGet reduced the runtime of that operation by 1000 times. – scottysseus Aug 08 '18 at 14:19
  • 2
    Is there any scenario, when orElse should be used instead of orElseGet? – lapkritinis Jan 19 '19 at 23:55
  • 2
    @lapkritinis I can't think of any scenario where `orElse()` would get you a different result, except if you plan to use the value of a not effectively final local variable, `orElseGet()` simply won't compile. I always use `orElse()` when using it with constants or variables because it's somewhat easier to read `orElse(42)` than `orElseGet(() -> 42)` – biziclop Jan 20 '19 at 23:07
  • 1
    Yes, there is. You could have wanted side effects as a result of executing the code inside orElse. You should probably refactor your code in this case though. – herbae Sep 13 '21 at 17:37
80

I reached here for the problem Kudo mentioned.

I'm sharing my experience for others.

orElse, or orElseGet, that is the question:

static String B() {
    System.out.println("B()...");
    return "B";
}

public static void main(final String... args) {

    System.out.println(
        Optional.of("A").orElse(B()) // B()'s gonna be invoked anyway
    );

    System.out.println("----");

    System.out.println(
        Optional.of("A").orElseGet(() -> B()) // B() won't be invoked
    );
}

prints

B()...
A
----
A

orElse evaluates the value of B() interdependently of the value of the optional. Thus, orElseGet is lazy.

Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
  • 8
    It is not a 'problem'. It is the simple fact that the argument for a method is evaluated prior to the method execution. If you pass `B()` to a method named `orElse()` or `abc()` it does not make any difference, `B()` gets evaluated. – jbx Jan 14 '17 at 11:37
  • 25
    The issue here is really the naming of the methods. The `or` prefix misleads developers (including myself when I asked the problem) into thinking that it is a short-circuiting operation, because that is what we are used to in boolean conditions. However, it is not, it is just a method name that has `or` in its prefix, so its arguments will be evaluated, irrespective of whether `Optional` is carrying a value or not. It is unfortunate that the naming is confusing, not that we can do anything about it. – jbx Nov 19 '17 at 21:15
  • 3
    Thanks for the answer. Must say the language designers did not think thru as to how confusing this could get. – Thyag Jan 20 '21 at 10:32
38

I would say the biggest difference between orElse and orElseGet comes when we want to evaluate something to get the new value in the else condition.

Consider this simple example -

// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
    value = oldValue;
} else {
    value = apicall().value;
}

Now let's transform the above example to using Optional along with orElse,

// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);

Now let's transform the above example to using Optional along with orElseGet,

// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);

When orElse is invoked, the apicall().value is evaluated and passed to the method. Whereas, in the case of orElseGet the evaluation only happens if the oldValue is empty. orElseGet allows lazy evaluation.

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
devang
  • 5,376
  • 7
  • 34
  • 49
  • 5
    I wasted a lot of times because of this "strange" behavior of ifElse(). I'd say it makes sense to prefer ifElseGet() over ifElse() – Enrico Giurin Jun 06 '17 at 14:40
7

First of all check the declaration of both the methods.

1) OrElse: Execute logic and pass result as argument.

public T orElse(T other) {    
 return value != null ? value : other;
}

2) OrElseGet: Execute logic if value inside the optional is null

public T orElseGet(Supplier<? extends T> other) {
  return value != null ? value : other.get(); 
}

Some explanation on above declaration: The argument of “Optional.orElse” always gets executed irrespective of the value of the object in optional (null, empty or with value). Always consider the above-mentioned point in mind while using “Optional.orElse”, otherwise use of “Optional.orElse” can be very risky in the following situation.

Risk-1) Logging Issue: If content inside orElse contains any log statement: In this case, you will end up logging it every time.

Optional.of(getModel())
   .map(x -> {
      //some logic
   })
  .orElse(getDefaultAndLogError());
 
getDefaultAndLogError() {
  log.error("No Data found, Returning default");
  return defaultValue;
}

Risk-2) Performance Issue: If content inside orElse is time-intensive: Time intensive content can be any i/o operations DB call, API call, file reading. If we put such content in orElse(), the system will end up executing a code of no use.

Optional.of(getModel())
   .map(x -> //some logic)
   .orElse(getDefaultFromDb());

getDefaultFromDb() {
   return dataBaseServe.getDefaultValue(); //api call, db call.
}

Risk-3) Illegal State or Bug Issue: If content inside orElse is mutating some object state: We might be using the same object at another place let say inside Optional.map function and it can put us in a critical bug.

List<Model> list = new ArrayList<>();
Optional.of(getModel())
  .map(x -> {
  })
  .orElse(get(list));

get(List < String > list) {
   log.error("No Data found, Returning default");
   list.add(defaultValue);
   return defaultValue;
}

Then, When can we go with orElse()? Prefer using orElse when the default value is some constant object, enum. In all above cases we can go with Optional.orElseGet() (which only executes when Optional contains empty value)instead of Optional.orElse(). Why?? In orElse, we pass default result value, but in orElseGet we pass Supplier and method of Supplier only executes if the value in Optional is null.

Key takeaways from this:

  1. Do not use “Optional.orElse” if it contains any log statement.
  2. Do not use “Optional.orElse” if it contains time-intensive logic.
  3. Do not use “Optional.orElse” if it is mutating some object state.
  4. Use “Optional.orElse” if we have to return a constant, enum.
  5. Prefer “Optional.orElseGet” in the situations mentioned in 1,2 and 3rd points.

I have explained this in point-2 (“Optional.map/Optional.orElse” != “if/else”) my medium blog. Use Java8 as a programmer not as a coder

anuj pradhan
  • 2,777
  • 4
  • 26
  • 31
Piyush N
  • 742
  • 6
  • 12
4

The difference is pretty subtle and if you dont pay much attention then you will keep it using in a wrong way.

Best way to understand the difference between orElse() and orElseGet() is that orElse() will always be executed if the Optional<T> is null or not, But orElseGet() will only be executed when Optional<T> is null.

The dictionary meaning of orElse is :- execute the part when something is not present, but here it contradicts, see the below example:

    Optional<String> nonEmptyOptional = Optional.of("Vishwa Ratna");
    String value = nonEmptyOptional.orElse(iAmStillExecuted());

    public static String iAmStillExecuted(){
    System.out.println("nonEmptyOptional is not NULL,still I am being executed");
    return "I got executed";
    }

Output: nonEmptyOptional is not NULL,still I am being executed


    Optional<String> emptyOptional = Optional.ofNullable(null);
    String value = emptyOptional.orElse(iAmStillExecuted());
    public static String iAmStillExecuted(){
    System.out.println("emptyOptional is NULL, I am being executed, it is normal as 
    per dictionary");
    return "I got executed";
    }

Output: emptyOptional is NULL, I am being executed, it is normal as per dictionary

For orElseGet() , The method goes as per dictionary meaning, The orElseGet() part will be executed only when the Optional is null.

Benchmarks:

+--------------------+------+-----+------------+-------------+-------+
| Benchmark          | Mode | Cnt | Score      | Error       | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark    | avgt | 20  | 60934.425  | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20  | 3.798      | ± 0.030     | ns/op |
+--------------------+------+-----+------------+-------------+-------+

Remarks: orElseGet() has clearly outperformed orElse() for our particular example.

Hope it clears the doubts of people like me who wants the very basic ground example :)

Vishwa Ratna
  • 5,567
  • 5
  • 33
  • 55
3

The following example should demonstrate the difference:

String destroyTheWorld() {
  // destroy the world logic
  return "successfully destroyed the world";
}

Optional<String> opt = Optional.of("Save the world");

// we're dead
opt.orElse(destroyTheWorld());

// we're safe    
opt.orElseGet(() -> destroyTheWorld());

The answer appears in the docs as well.

public T orElseGet(Supplier<? extends T> other):

Return the value if present, otherwise invoke other and return the result of that invocation.

The Supplier won't be invoked if the Optional presents. whereas,

public T orElse(T other):

Return the value if present, otherwise return other.

If other is a method that returns a string, it will be invoked, but it's value won't be returned in case the Optional exists.

Maroun
  • 94,125
  • 30
  • 188
  • 241
  • In the given example, 'we are dead' in both cases. Optional opt = Optional.empty(); will return optional with value null. orElse logic inside value != null ? value : other. orElseGet logic inside value != null ? value : supplier.get(). In the given example, both invoke destroyTheWorld() – ABHIJITH KINI Jul 22 '20 at 13:14
  • @ABHIJITHKINI In the second case, the method will not be actually executed. Don't worry, I saved the world. – Maroun Jul 22 '20 at 13:25
  • 1
    In the given example, It will; Please execute below : Optional opt = Optional.empty(); opt.orElseGet(() -> destroyTheWorld()); in the console : successfully destroyed the world because opt value is NULL. – ABHIJITH KINI Jul 23 '20 at 10:33
1

Considering the following code:

import java.util.Optional;

// one class needs to have a main() method
public class Test
{
  public String orelesMethod() {
    System.out.println("in the Method");
    return "hello";
  }

  public void test() {
    String value;
    value = Optional.<String>ofNullable("test").orElseGet(this::orelesMethod);
    System.out.println(value); 

    value = Optional.<String>ofNullable("test").orElse(orelesMethod());
    System.out.println(value); 
  }

  // arguments are passed using the text field below this editor
  public static void main(String[] args)
  {
    Test test = new Test();

    test.test();
  }
}

if we get value in this way: Optional.<String>ofNullable(null), there is no difference between orElseGet() and orElse(), but if we get value in this way: Optional.<String>ofNullable("test"), orelesMethod() in orElseGet() will not be called but in orElse() it will be called

ratzip
  • 1,571
  • 7
  • 28
  • 53
1

They are both used to return a default value for one Optional, but if a method is used to produce the dafault value:

  • orElse: always executes the method, also if the Optional is not empty
  • orElseGet: executes it only if the Optional is empty (+ Performance!)

see this example (class OptionalExample):

public static void main(String[] args) {

    Optional<String> optionalNotEmpty = Optional.of("StringVal");
    
    // orElse: getDefaultValue called (useless)
    System.out.println(optionalNotEmpty.orElse(getDefaultValue()));
    
    // orElseGet: getDefaultValue not called (better solution)
    System.out.println(optionalNotEmpty.orElseGet(OptionalExample::getDefaultValue));
}

public static String getDefaultValue() {
    System.out.println("called");
    return "default value";
}

Output:

called
StringVal
StringVal
MauroB
  • 510
  • 4
  • 12