1. Introduction
The API of Optional typically has two methods that can cause confusion: orElse() and orElseGet().
In this quick tutorial, we’ll look at the difference between these two and explore when to use each one.
2. Signatures
First, let’s start with the basics by looking at their signatures:
public T orElse(T other)
public T orElseGet(Supplier<? extends T> other)
Clearly, orElse() takes any parameter of a type T, whereas orElseGet() accepts a functional interface of type Supplier that returns an object of type T.
Based on their Javadocs:
- orElse(): returns the value if present, otherwise returns other
- orElseGet(): returns the value if present, otherwise invokes other and returns the result of its invocation
3. Differences
It’s easy to be a bit confused by these simplified definitions, so let’s dig a little deeper and look at some actual usage scenarios.
3.1. orElse()
Assuming we have our logger configured properly, let’s start with writing a simple piece of code:
String name = Optional.of("baeldung")
.orElse(getRandomName());
Notice that getRandomName() is a method which returns a random name from a *List
public String getRandomName() {
LOG.info("getRandomName() method - start");
Random random = new Random();
int index = random.nextInt(5);
LOG.info("getRandomName() method - end");
return names.get(index);
}
On executing our code, we’ll find the below messages printed in the console:
getRandomName() method - start
getRandomName() method - end
The variable name will hold “baeldung” at the end of the code execution.
With it, we can easily infer that the parameter of orElse() is evaluated, even when having a non-empty Optional.
3.2. orElseGet()
Now let’s try writing similar code using orElseGet():
String name = Optional.of("baeldung")
.orElseGet(() -> getRandomName());
The above code won’t invoke the getRandomName() method.
Remember (from the Javadoc) that the Supplier method passed as an argument is only executed when an Optional value isn’t present.
Therefore, using orElseGet() for our case will save us the time involved in computing a random name.
4. Measuring Performance Impact
Now, to also understand the differences in performance, let’s use JMH and see some actual numbers:
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseBenchmark() {
return Optional.of("baeldung").orElse(getRandomName());
}
And orElseGet():
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseGetBenchmark() {
return Optional.of("baeldung").orElseGet(() -> getRandomName());
}
While executing our benchmark methods, we get:
Benchmark Mode Cnt Score Error Units
orElseBenchmark avgt 20 60934.425 ± 15115.599 ns/op
orElseGetBenchmark avgt 20 3.798 ± 0.030 ns/op
As we can see, the performance impact might be substantial, even for such a simple use-case scenario.
The numbers above might slightly vary; however, orElseGet() has clearly outperformed orElse() for our particular example.
After all, orElse() involves the computation of the getRandomName() method for each run.
5. What’s Important?
Apart from the performance aspects, other factors worth considering include:
- What if the method would execute some additional logic? E.g. making some DB inserts or updates
- Even when we assign an object to the orElse() parameter, we’re still creating “Other” object for no reason*:*
String name = Optional.of("baeldung").orElse("Other")
That’s why it’s important for us to make a careful decision between orElse() and orElseGet() depending on our needs. By default, it makes more sense to use orElseGet() every time, unless the default object is already constructed and directly accessible.
6. Conclusion
In this article, we learned the nuances between the Optional orElse() and OrElseGet() methods. We also discussed how such simple concepts can sometimes have a deeper meaning.
As always, the complete source code can be found over on Github.