nt dmr
nt dmr

Reputation: 3

Java 8: Stream and filter based on optional conditions

Example: Filter a list of products that have a price based on fromPrice and toPrice. They could either both be supplied, or just one.

  1. Find all products whose price is greater than fromPrice
  2. Find all products whose price is less than toPrice
  3. Find all products whose price is between fromPrice and toPrice

Product:

public class Product {

    private String id;

    private Optional<BigDecimal> price;

    public Product(String id, BigDecimal price) {
        this.id = id;
        this.price = Optional.ofNullable(price);
    }
}

PricePredicate:

public class PricePredicate {

    public static Predicate<? super Product> isBetween(BigDecimal fromPrice, BigDecimal toPrice) {
        if (fromPrice != null && toPrice != null) {
            return product -> product.getPrice().isPresent() && product.getPrice().get().compareTo(fromPrice) >= 0 &&
                    product.getPrice().get().compareTo(toPrice) <= 0;
        }
        if (fromPrice != null) {
            return product -> product.getPrice().isPresent() && product.getPrice().get().compareTo(fromPrice) >= 0;
        }
        if (toPrice != null) {
            return product -> product.getPrice().isPresent() && product.getPrice().get().compareTo(toPrice) <= 0;
        }
        return null;
    }
}

Filters:

return this.products.stream().filter(PricePredicate.isBetween(fromPrice, null)).collect(Collectors.toList());

return this.products.stream().filter(PricePredicate.isBetween(null, toPrice)).collect(Collectors.toList());

return this.products.stream().filter(PricePredicate.isBetween(fromPrice, toPrice)).collect(Collectors.toList());

Is there a way to improve my Predicate instead of having the if not null checks? Anything that can be done with optionals?

Upvotes: 0

Views: 2960

Answers (2)

JB Nizet
JB Nizet

Reputation: 691755

No, Optional is not designed to replace null checks.

But your code can be improved by avoiding duplication, and by avoiding to return null (which is clearly not a valid value for a Predicate) if both arguments are null:

public static Predicate<Product> isBetween(BigDecimal fromPrice, BigDecimal toPrice) {
    Predicate<Product> result = product -> true;

    if (fromPrice != null) {
        result = result.and(product -> product.getPrice().isPresent() && product.getPrice().get().compareTo(fromPrice) >= 0);
    }

    if (toPrice != null) {
        result = result.and(product -> product.getPrice().isPresent() && product.getPrice().get().compareTo(toPrice) <= 0);
    }

    return result;
}

Upvotes: 2

Adrian
Adrian

Reputation: 3134

You can use Apache Commons Lang, it offers null safe comparison:

ObjectUtils.compare(from, to)

null is assumed to be less than a non-value

Upvotes: 0

Related Questions