RonAn
RonAn

Reputation: 61

How to fetch the data from a collection of optionals?

I have a method returning a collection of products:

Collection<Product> getProducts() { ... }  

Each product may have a guarantee. But it is not required.

interface Product {
    Optional<Guarantee> getGuarantee();
}

Now I need to go through all the products and check if the quarantees have expired. The non-expired ones should be collected into a list.

This is what I do:

List<Optional<Guarantee>> optionalGar = getProducts().stream()
      .map(f -> f.getGuarantee()).collect(Collectors.toList());

List<Guarantee> gar = optionalGar.stream()    
      .map(op -> op.orElse(null))             
      .filter(Objects::nonNull)
      .filter(g -> !g.hasExpired())
      .collect(Collectors.toList());

Is there any way to avoid using .orElse(null)?

(Replacing it by op.get() would cause an exception in case the optional is empty)

P.S: I'm free to chose between Java 8 and Java 9 so both solutions (not sure if the'll be different) are welcome

Upvotes: 5

Views: 877

Answers (1)

ETO
ETO

Reputation: 7299

Java 8

List<Guarantee> expiredGuarantees = getProducts().stream()
                                                 .map(Product::getGuarantee)    
                                                 .filter(Optional::isPresent)
                                                 .map(Optional::get)
                                                 .filter(not(Guarantee::hasExpired))
                                                 .collect(toList());

Java 9

Java9 has got Optional::stream. So you can replace filtering and mapping with single flatMap:

List<Guarantee> expiredGuarantees = getProducts().stream()
                                                 .map(Product::getGuarantee)    
                                                 .flatMap(Optional::stream)
                                                 .filter(not(Guarantee::hasExpired))
                                                 .collect(toList());

Note

Java 8 does not have Predicates.not method. It's included since 11th version only.

By adding the following method to your project you'll be able to use it with the solutions above.

public static <T> Predicate<T> not(Predicate<T> predicate) { 
    return predicate.negate();
}

Update

Although this is not the CodeReview community, here are some notes on your code:

  • By combining the two pipelines into a single your code will be cleaner (in this particular case).
  • Prefer a method reference over a lambda when possible
  • Give appropriate names to your variables so you'll make your code easier to maintain

Upvotes: 7

Related Questions