Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

findAny orElse after filter

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?

like image 906
paul Avatar asked Jan 04 '16 09:01

paul


People also ask

Which is faster findAny or findFirst?

Stream findFirst() vs findAny() – ConclusionUse findAny() to get any element from any parallel stream in faster time.

What is the difference between the findFirst () and findAny () method?

The findAny() method returns any element from a Stream, while the findFirst() method returns the first element in a Stream.

How do you use optional orElse?

The orElse() method of java. util. Optional class in Java is used to get the value of this Optional instance, if present. If there is no value present in this Optional instance, then this method returns the specified value.


2 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.

like image 199
Tagir Valeev Avatar answered Sep 28 '22 00:09

Tagir Valeev


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.

like image 25
Holger Avatar answered Sep 27 '22 22:09

Holger