The following scenario has multiple Optionals, a real example could be where you are calling
multiple validation checks where each check returns an Optional<T>
. You then want to check
if each Optional contains a value or is empty - returning the first one that has a value. If none
contain a value (ie. are instances of Optional.empty
) then you return a default value.
In this example we are using Optional<Single>
where Single
is an RxJava Observer that returns
a single value.
import com.google.common.collect.ImmutableList;
import io.reactivex.Single;
import java.util.Optional;
import java.util.stream.Stream;
public class ChainedOptionals {
public static void main(String[] args) {
Optional<Single> opt1 = Optional.empty();
Optional<Single> opt2 = Optional.empty();
Optional<Single> opt3 = Optional.of(Single.just("3"));
Optional<Single> opt4 = Optional.empty();
Optional<Single> opt5 = Optional.empty();
// This is the interesting bit
Optional<Single> optRes = ImmutableList
.of(opt1, opt2, opt3, opt4, opt5).stream()
.flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
// If option is not empty then return its value or else return a Single containing "NONE"
Single single = optRes.map(result -> result).orElse(Single.just("NONE"));
// Get the value of the Single
System.out.println(single.blockingGet());
}
}
Note that the following line is where the magic happens.
.flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
The flatMap
gets each optional value (if we used map
we would get each Optional
rather than its value). We then map
over each value and if it contains something we create a stream containing that single Optional(value)
using Stream::of
. We don't find any values in the stream then we create a stream containing a single Optional.empty
using orElseGet
. The findFirst
method of Stream
returns the first value in the stream so the sequence with which we have placed each optx
in the stream is followed.
The problem with the above is that it is not very efficient if we simply want to return the first non-empty Optional we find. This is because opt1
, opt2
, opt3
, opt4
, opt5
are all evaluated first. Then we construct a list containing the results of each one. Then we map over the stream. The stream is lazily evaluated yes, and so it does not get as far as opt4
since it returns once it finds that opt3
is not empty, but we already already computed opt4
and opt5
already so that was a waste of time.
The following is an attempt at being more efficient making the assumption that methods 4 and 5 are never invoked. It turns out however that using Stream.of
will in fact invoke all the methods and so this is not improvement either.
public class LazyOptional {
static void print(Object o) { System.out.println(o); }
static Optional<Integer> method1() { print("1 invoked"); return Optional.empty(); }
static Optional<Integer> method2() { return Optional.empty(); }
static Optional<Integer> method3() { return Optional.of(5); }
static Optional<Integer> method4() { return Optional.of(10); }
static Optional<Integer> method5() { return Optional.empty(); }
public static void main(String[] args) {
Stream<Optional<Integer>> stream = Stream.of(method1(), method2(), method3(), method4(), method5());
Optional<Integer> result = stream
.peek(opt -> System.out.println("will compute " + opt))
.flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
.findAny();
print(result.get());
}
}
Notice that we have used the peek
method as a way to inspect what elements in the stream actually get evaluated. We could also have just printed out when each method got invoked like this.
static Optional<Integer> method1() { print("1 invoked"); return Optional.empty(); }
static Optional<Integer> method2() { print("2 invoked"); return Optional.empty(); }
static Optional<Integer> method3() { print("3 invoked"); return Optional.of(5); }
static Optional<Integer> method4() { print("4 invoked"); return Optional.of(10); }
static Optional<Integer> method5() { print("5 invoked"); return Optional.empty(); }
The following approach combines the use of Lambdas into the mix. We are still using Stream.of
to construct the stream but now it contains references to lambda expressions rather than methods. This time, opt4
and opt5
are never invoked since we are using findFirst
to get the first element in the stream and opt3
returns a value so we quit there.
public class LazyOptional {
static void print(Object o) { System.out.println(o); }
// A functional interface used to define each lambda
@FunctionalInterface
interface OptionalFunction<T> {
Optional<T> compute(T t);
}
public static void main(String[] args) {
// some lambdas
OptionalFunction<Integer> opt1 = (integer) -> { print("opt1 invoked"); return Optional.empty(); };
OptionalFunction<Integer> opt2 = (integer) -> { print("opt2 invoked"); return Optional.empty(); };
OptionalFunction<Integer> opt3 = (integer) -> { print("opt3 invoked"); return Optional.of(3 * integer); };
OptionalFunction<Integer> opt4 = (integer) -> { print("opt4 invoked"); return Optional.of(4 * integer); };
OptionalFunction<Integer> opt5 = (integer) -> { print("opt5 invoked"); return Optional.of(5 * integer); };
Stream<OptionalFunction<Integer>> stream = Stream.of(opt1, opt2, opt3, opt4, opt5);
Optional<Integer> result = stream
.flatMap(opt -> opt.compute(10).map(Stream::of).orElseGet(Stream::empty))
.findFirst();
print(result.get());
}
}
Running the above prints out.
opt1 invoked
opt2 invoked
opt3 invoked
30
Note that Stream
also has a parallel
function which allows us to take advantage of multiple cores.
Optional<Integer> result = stream
.parallel()
.flatMap(opt -> opt.compute(10).map(Stream::of).orElseGet(Stream::empty))
.findFirst();
However, this is not what we want in this scenario since it actually evaluates all our lambdas as running it demonstrates.
opt3 invoked
opt5 invoked
opt2 invoked
opt4 invoked
opt1 invoked
30
Even though the order in which the lambdas are all run is not deterministic, we always return the first non-empty element we find in the stream since this is the bahviour of findFirst
as these multiple runs demonstrate.
opt1 invoked
opt4 invoked
opt2 invoked
opt5 invoked
opt3 invoked
30
opt3 invoked
opt4 invoked
opt2 invoked
opt1 invoked
opt5 invoked
30
opt2 invoked
opt5 invoked
opt4 invoked
opt3 invoked
opt1 invoked
30
Stream
has another method called findAny
, which will return any non-empty element (in our example). If we do not parallelise the execution of the stream then the behaviour is not different from findFirst
since we evaluate the stream in sequence and opt3
will always be the any we find. However, if we combine findAny
with parallel
then we can see that the result we return from the stream as as undeterministic as the order in which the lamdas are run.
Optional<Integer> result = stream
.parallel()
.flatMap(opt -> opt.compute(10).map(Stream::of).orElseGet(Stream::empty))
.findAny();
Will produce, for example.
opt3 invoked
opt4 invoked
opt1 invoked
opt2 invoked
opt5 invoked
50
opt3 invoked
opt4 invoked
opt2 invoked
opt1 invoked
opt5 invoked
40
opt3 invoked
opt4 invoked
opt5 invoked
opt2 invoked
opt1 invoked
50
opt3 invoked
opt1 invoked
opt5 invoked
opt2 invoked
opt4 invoked
30
The java.util.function
package provides several Functional interfaces which satisfy most developers’ needs in providing target types for lambda expressions and method references. Each of these interfaces is general and abstract, making them easy to adapt for most lambda expression use cases. Developers should explore this package before creating new functional interfaces.
The following demonstrates our example but using Function<T,R>
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
public class LazyValidators {
static void print(Object o) { System.out.println(o); }
public static void main(String[] args) {
Function<String, Optional<String>> validatorFunction1 = (string) -> { print("validator 1 invoked"); return Optional.empty(); };
Function<String, Optional<String>> validatorFunction2 = (string) -> { print("validator 2 invoked"); return Optional.of(string + " 2"); };
Function<String, Optional<String>> validatorFunction3 = (string) -> { print("validator 3 invoked"); return Optional.empty(); };
Stream<Function<String, Optional<String>>> stream = Stream.of(validatorFunction1, validatorFunction2, validatorFunction3);
Optional<String> result = stream
.flatMap(opt -> opt.apply("Hello").map(Stream::of).orElseGet(Stream::empty))
.findFirst();
print(result.get());
}
}
Not sure why not using
Optional result = stream
.filter(opt -> opt.apply("Hello").isPresent())
.findFirst();