it-swarm.com.ru

Объединение лямбда-предикатов Java 8 с логическими операторами

У меня есть Stream<SomeClass> stream, тогда как SomeClass имеет логические методы isFoo() и isBar().

Я хотел бы проверить, что все элементы в потоке имеют isFoo() и isBar() равны true. Я могу проверить эти условия индивидуально через SomeClass:isFoo и SomeClass::isBar lambdas.

Но как мне объединить эти две лямбды с логическим оператором, таким как and/&&?

Один очевидный способ - написать дополнительную лямбду:

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

Но я хотел бы избежать написания дополнительной лямбды.

Другой способ - привести к Predicate<? super SomeClass>:

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

Есть ли лучший способ - без приведения и явных лямбд?

8
lexicore

Если бы существовал гипотетический метод Predicate.of, вы могли бы написать:

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

Этого не существует, но вы можете написать это сами.

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

Тем не менее, я бы лично пошел с вашим первым вариантом.

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

Ссылки на метод :: хороши, но иногда приходится писать явные лямбды.

12
John Kugelman

Возможно, это не квалифицируется как ответ (пожалуйста, не голосуйте, если это не так), но у меня вроде была такая же необходимость - собрать несколько предикатов, и я просто написал для этого небольшую утилиту.

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

Вы обязательно должны использовать фильтр метод Stream. Фильтр выберите элементы списка, которые соответствуют данному предикату. 

Эта строка выбирает элементы, которые являются одновременно Foo AND Bar:

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

Выводом является Stream, поэтому вы должны собрать элементы в List, чтобы фактически использовать их:

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

Чтобы проверить, являются ли все элементы потока как Foo, так и Bar, вы можете сравнить размер исходного списка с размером отфильтрованного потока:

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

Вы можете фильтровать и собирать только размер списка и сравнивать с исходным списком:

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

0
GeneralBecos