«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"));
}

В этом примере мы отфильтровали наш список имен, чтобы оставить только имена, начинающиеся с «A» с использованием предиката:

name -> name.startsWith("A")

Но что, если мы хотим применить несколько предикатов?

3. Множественные фильтры

Если мы хотим применить несколько предикатов, один из вариантов — просто объединить несколько фильтров в цепочку: «A» и иметь длину меньше 5.

@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"));
}

Мы использовали два фильтра — по одному для каждого предиката.

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()

В этом примере мы явно определим наши предикаты, а затем объединим их с помощью Predicate.and():

Как мы видим, синтаксис довольно интуитивно понятен. , а имена методов предполагают тип операции. Используя 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"));
}

5.2. Predicate.or()

Мы также можем использовать Predicate.or() для объединения предикатов.

Извлечем имена, начинающиеся с «J», а также имена длиной меньше 4:

5.3. Predicate.negate()

@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"));
}

Мы также можем использовать Predicate.negate() при комбинировании наших предикатов:

Здесь мы использовали комбинацию or() и negate() для фильтрации списка по имена, начинающиеся с «J» или имеющие длину не менее 4.

@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"));
}

5.4. Объединение встроенных предикатов

Нам не нужно явно определять наши предикаты для использования and(), or() и negate().

Мы также можем использовать их встроенно, приведя предикат:

6. Объединение набора предикатов

@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"));
}

Наконец, давайте посмотрим, как объединить набор предикатов в цепочку, сократив их.

В следующем примере у нас есть список предикатов, которые мы объединили с помощью 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"));
}

Но это будет другое если мы хотим объединить их с помощью Predicate.or():

x->true

7. Заключение

@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"));
}

В этой статье мы рассмотрели различные способы объединения предикатов в цепочку в Java 8, используя filter(), создавая сложные предикаты, и объединение предикатов.

Полный исходный код доступен на GitHub.

«