lexicore
lexicore

Reputation: 43651

Combining Java 8 lambda predicates with logical operators

I have a Stream<SomeClass> stream whereas SomeClass has boolean methods isFoo() and isBar().

I'd like to check that all elements in the stream have both isFoo() and isBar() equals to true. I can check this conditions individually via SomeClass:isFoo and SomeClass::isBar lambdas.

But how would I combine these two lambdas with a logical operator like and/&&?

One obvious way is to write an extra lambda:

stream.allMatch(item -> item.isFoo() && item.isBar());

But I'd like to avoid writing an extra lambda.

Another way is to cast to Predicate<? super SomeClass>:

stream.allMatch(((Predicate<? super SomeClass>) SomeClass::isFoo).and(SomeClass::isBar));

Is there a better way - without casts and explicit lambdas?

Upvotes: 9

Views: 17156

Answers (4)

Eugene
Eugene

Reputation: 120848

This might not qualify as the answer (please don't up vote it if it does not), but I sort of had the same need, to munch together some predicates and I just wrote a small utility for that.

private static <T> Predicate<T> andPredicates(Predicate<T>... predicates) {
    return Arrays.stream(predicates).reduce(Predicate::and).orElse(x -> true);
}

Upvotes: 12

GeneralBecos
GeneralBecos

Reputation: 2556

You could filter and only collect the size of the list and compare with the original list :

long count = list.stream().filter(SomeClass::isFoo).filter(SomeClass::isBar).collect(Collectors.counting());

Upvotes: 1

Andrea
Andrea

Reputation: 795

You should definitely use the filter method of Stream. Filter select the elements of the list that match the given predicate.

This line select the elements which are Foo AND Bar at the same time:

list.stream().filter(SomeClass::isFoo).filter(SomeClass::isBar)

The output is a Stream, so you should collect the elements in a List to actually use them:

 list.stream().filter(SomeClass::isFoo).filter(SomeClass::isBar).collect(Collectors.toList());

In order to check if all the elements of the stream are both Foo and Bar, you could compare the size of the original list with the size of the filtered stream:

public static Boolean isFooBar(List<SomeClass> list) {
    return list.size() == list.stream().filter(SomeClass::isFoo).filter(SomeClass::isBar).count();
}

Upvotes: 0

John Kugelman
John Kugelman

Reputation: 361537

If there were a hypothetical Predicate.of method, you could write:

stream.allMatch(Predicate.of(SomeClass::isFoo).or(SomeClass::isBar));

It doesn't exist, but you could write it yourself.

public final class Predicates {
    public static <T> Predicate<T> of(Predicate<T> predicate) {
        return predicate;
    }
}

That said, I would personally go with your first option.

stream.allMatch(item -> item.isFoo() && item.isBar());

:: method references are nice but sometimes you have to write explicit lambdas.

Upvotes: 13

Related Questions