Skip to content

Instantly share code, notes, and snippets.

@jonjack
Last active March 23, 2021 01:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonjack/b3fd61a1d69a2215f44eaf91bfdf977c to your computer and use it in GitHub Desktop.
Save jonjack/b3fd61a1d69a2215f44eaf91bfdf977c to your computer and use it in GitHub Desktop.
Example of how to check multiple Optional values in a chain and return a default value if none contain a value.

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

Using java.util.function.Function<T,R>

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());
    }
}
@xuelishen
Copy link

Not sure why not using

Optional result = stream
.filter(opt -> opt.apply("Hello").isPresent())
.findFirst();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment