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 those two and explore when to use each one.
2. Signatures
Let’s first 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.
Now, based on their Javadocs:
- orElse(): returns the value if present, otherwise return other
- orElseGet(): returns the value if present, otherwise invoke other and return the result of its invocation
3. Differences
It’s easy to be a bit confused by this 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<String>of names:
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 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 will not invoke getRandomName() method.
Remember (from the Javadoc) that the Supplier method passed as an argument is only executed when an Optional value is not present.
Using orElseGet() for our case will, therefore, save us some 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.
Afterall, orElse() involves computation of getRandomName() method for each run.
5. What’s Important?
Apart from the performance aspects, other worth-considering factors involve:
- What if the method would execute some additional logic? E.g. making some DB inserts or updates
- Even when we assign an object to orElse() parameter:
String name = Optional.of("baeldung").orElse("Other")
we’re still creating “Other” object for no reason
And that’s why it is important for us to make a careful decision among 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 accessible directly.
6. Conclusion
In this article, we’ve learned the nuances between Optional orElse() and OrElseGet() methods. We also noticed how sometimes such simple concepts can have a deeper meaning.
As always, the complete source code can be found over on Github.