findAny orElse after filter

21,104

Solution 1

As Tagir explained, using orElse(expression) always causes the evaluation of expression before invoking the method orElse and you have to use orElseGet(() -> expression) instead to defer the evaluation of the expression.

However, this is an unnecessary use of the Stream API. If you want to evaluate a single item, you don’t need to create a single-element stream just to call findAny afterwards. You can create an Optional in the first place:

return Optional.of(getObjectAttributeValue(product, matchCriteria.getFieldName()))
     .map(o -> isIngredientRestricted(matchCriteria, (List<String>)o))
     .filter(b -> b)
     .orElseGet(() -> isCommercialHierarchyInfoRestricted(product, matchCriteria));

However, even that is an unnecessary complication compared to the equivalent ordinary Java language construct:

return isIngredientRestricted(matchCriteria,
           (List<String>)getObjectAttributeValue(product, matchCriteria.getFieldName()))
  ||  isCommercialHierarchyInfoRestricted(product, matchCriteria);

This does exactly the same without the need for additional APIs nor lambda expressions. The || operator also guarantees that the second expression won’t get evaluated, if the first one evaluates to true.

Solution 2

You actually need to use orElseGet:

.findAny().orElseGet(() -> isCommercialHierarchyInfoRestricted(product, matchCriteria));

In Java method argument is always evaluated prior to method call even if it's unnecessary inside the method, so you cannot avoid evaluating the orElse argument. That's why orElseGet exists: its argument is the function and the function can be not executed at all when it's not necessary.

Share:
21,104
paul
Author by

paul

https://github.com/politrons/reactive

Updated on July 10, 2022

Comments

  • paul
    paul almost 2 years

    I'm using Stream filter findAny.orElse, but it's not working as I expect, so I presume I'm not understanding how really works. Here my code

    return Stream.of(getObjectAttributeValue(product, matchCriteria.getFieldName()))
                 .map(o -> isIngredientRestricted(matchCriteria, (List<String>) o))
                 .filter(Boolean::valueOf)
                 .findAny().orElse(isCommercialHierarchyInfoRestricted(product, matchCriteria));
    

    Basically what I was expecting was that if the first map emit a Boolean false, then it will be filter so then the findAny would not find any optional, so the orElse would be invoked. But even having a true in the filter the isCommercialHierarchyInfoRestricted is invoked.

    Any idea what I'm doing wrong?