1. Обзор
В этом кратком руководстве мы обсудим различные способы объединения предикатов
в цепочку в Java 8.
2. Основной пример
Во- первых, давайте посмотрим, как использовать простой предикат
для фильтрации списка
имен:
@Test
public void whenFilterList_thenSuccess(){
List<String> names = Arrays.asList("Adam", "Alexander", "John", "Tom");
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
assertEquals(2, result.size());
assertThat(result, contains("Adam","Alexander"));
}
В этом примере мы отфильтровали наш список
имен, чтобы оставить только имена, начинающиеся с «А», используя предикат
:
name -> name.startsWith("A")
Но что, если мы хотим применить несколько предикатов
?
3. Несколько фильтров
Если мы хотим применить несколько Predicates
, один из вариантов — просто объединить несколько фильтров в цепочку:
@Test
public void whenFilterListWithMultipleFilters_thenSuccess(){
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.filter(name -> name.length() < 5)
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
Теперь мы обновили наш пример, чтобы отфильтровать наш список, извлекая имена, начинающиеся с «А» и имеющие длину меньше 5.
Мы использовали два фильтра — по одному для каждого Predicate
.
4. Сложный предикат
Теперь вместо использования нескольких фильтров мы можем использовать один фильтр со сложным предикатом
:
@Test
public void whenFilterListWithComplexPredicate_thenSuccess(){
List<String> result = names.stream()
.filter(name -> name.startsWith("A") && name.length() < 5)
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
Этот вариант более гибкий, чем первый, так как мы можем использовать побитовые операции для построения предиката
настолько сложного, насколько мы хотим.
5. Объединение предикатов
Далее, если мы не хотим создавать сложный предикат
с использованием побитовых операций, в Java 8 Predicate
есть полезные методы, которые мы можем использовать для объединения предикатов
.
Мы будем комбинировать предикаты
, используя методы Predicate.and()
, Predicate.or()
и Predicate.negate().
5.1. Предикат.и()
В этом примере мы явно определим наши предикаты
, а затем объединим их с помощью Predicate.and():
@Test
public void whenFilterListWithCombinedPredicatesUsingAnd_thenSuccess(){
Predicate<String> predicate1 = str -> str.startsWith("A");
Predicate<String> predicate2 = str -> str.length() < 5;
List<String> result = names.stream()
.filter(predicate1.and(predicate2))
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
Как мы видим, синтаксис довольно интуитивно понятен, а имена методов подсказывают тип операции. Используя and()
, мы отфильтровали наш список
, извлекая только имена, которые удовлетворяют обоим условиям.
5.2. Предикат.или()
Мы также можем использовать Predicate.or()
для объединения предикатов.
Извлечем имена, начинающиеся с «J», а также имена длиной меньше 4:
@Test
public void whenFilterListWithCombinedPredicatesUsingOr_thenSuccess(){
Predicate<String> predicate1 = str -> str.startsWith("J");
Predicate<String> predicate2 = str -> str.length() < 4;
List<String> result = names.stream()
.filter(predicate1.or(predicate2))
.collect(Collectors.toList());
assertEquals(2, result.size());
assertThat(result, contains("John","Tom"));
}
5.3. Предикат.отрицать()
Мы также можем использовать Predicate.negate()
при объединении наших предикатов
:
@Test
public void whenFilterListWithCombinedPredicatesUsingOrAndNegate_thenSuccess(){
Predicate<String> predicate1 = str -> str.startsWith("J");
Predicate<String> predicate2 = str -> str.length() < 4;
List<String> result = names.stream()
.filter(predicate1.or(predicate2.negate()))
.collect(Collectors.toList());
assertEquals(3, result.size());
assertThat(result, contains("Adam","Alexander","John"));
}
Здесь мы использовали комбинацию or()
и negate()
для фильтрации списка
по именам, начинающимся с «J» или имеющим длину не менее 4.
5.4. Объединить встроенные предикаты
Нам не нужно явно определять наши предикаты
для использования and(),
or()
и negate().
Мы также можем использовать их встроенно, приведя предикат
:
@Test
public void whenFilterListWithCombinedPredicatesInline_thenSuccess(){
List<String> result = names.stream()
.filter(((Predicate<String>)name -> name.startsWith("A"))
.and(name -> name.length()<5))
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
6. Объединение набора предикатов
Наконец, давайте посмотрим, как связать коллекцию предикатов
, сократив их.
В следующем примере у нас есть список
предикатов , которые мы объединили с помощью Predicate.and
()
:
@Test
public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){
List<Predicate<String>> allPredicates = new ArrayList<Predicate<String>>();
allPredicates.add(str -> str.startsWith("A"));
allPredicates.add(str -> str.contains("d"));
allPredicates.add(str -> str.length() > 4);
List<String> result = names.stream()
.filter(allPredicates.stream().reduce(x->true, Predicate::and))
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Alexander"));
}
Обратите внимание, что мы используем нашу базовую идентичность как:
x->true
Но все будет иначе, если мы захотим объединить их с помощью Predicate.or()
:
@Test
public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){
List<String> result = names.stream()
.filter(allPredicates.stream().reduce(x->false, Predicate::or))
.collect(Collectors.toList());
assertEquals(2, result.size());
assertThat(result, contains("Adam","Alexander"));
}
7. Заключение
В этой статье мы рассмотрели различные способы связывания предикатов в Java 8, используя filter(),
создавая сложные предикаты
и комбинируя предикаты.
Полный исходный код доступен на GitHub .