Home » Java » findAny orElse after filter

findAny orElse after filter

Posted by: admin December 28, 2021 Leave a comment

Questions:

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?

Answers:

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.

###

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.