Цепочка предикатов Java 8

Цепочка предикатов Java 8

1. обзор

В этом кратком руководствеwe’ll discuss different ways to chain Predicates in Java 8.

2. Основной пример

Во-первых,let’s see how to use a simple Predicate для фильтрацииList имен:

@Test
public void whenFilterList_thenSuccess(){
   List names = Arrays.asList("Adam", "Alexander", "John", "Tom");
   List result = names.stream()
     .filter(name -> name.startsWith("A"))
     .collect(Collectors.toList());

   assertEquals(2, result.size());
   assertThat(result, contains("Adam","Alexander"));
}

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

name -> name.startsWith("A")

Но что, если бы мы хотели применить несколькоPredicates?

3. Несколько фильтров

Если бы мы хотели применить несколькоPredicates,one option is to simply chain multiple filters:

@Test
public void whenFilterListWithMultipleFilters_thenSuccess(){
    List result = names.stream()
      .filter(name -> name.startsWith("A"))
      .filter(name -> name.length() < 5)
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

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

Мы использовали два фильтра - по одному на каждыйPredicate.

4. КомплексPredicate

Теперь вместо использования нескольких фильтровwe can use one filter with a complex Predicate:

@Test
public void whenFilterListWithComplexPredicate_thenSuccess(){
    List result = names.stream()
      .filter(name -> name.startsWith("A") && name.length() < 5)
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

Этот вариант более гибкий, чем первый, посколькуwe can use bitwise operations to build the Predicate настолько сложен, насколько мы хотим.

5. ОбъединениеPredicates

Далее, если мы не хотим строить сложныйPredicate с помощью побитовых операций, в Java 8Predicate есть полезные методы, которые мы можем использовать для объединенияPredicates.

Мы объединимPredicates, используя методыPredicate.and(),Predicate.or() иPredicate.negate().

5.1. Predicate.and()с

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

@Test
public void whenFilterListWithCombinedPredicatesUsingAnd_thenSuccess(){
    Predicate predicate1 =  str -> str.startsWith("A");
    Predicate predicate2 =  str -> str.length() < 5;

    List result = names.stream()
      .filter(predicate1.and(predicate2))
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

Как мы видим, синтаксис довольно интуитивен, а имена методов указывают на тип операции. Используяand(), мы отфильтровали нашList, извлекая только имена, которые удовлетворяют обоим условиям.

5.2. Predicate.or()с

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

Давайте извлечем имена, начинающиеся с буквы «J», а также имена, длина которых меньше 4:

@Test
public void whenFilterListWithCombinedPredicatesUsingOr_thenSuccess(){
    Predicate predicate1 =  str -> str.startsWith("J");
    Predicate predicate2 =  str -> str.length() < 4;

    List result = names.stream()
      .filter(predicate1.or(predicate2))
      .collect(Collectors.toList());

    assertEquals(2, result.size());
    assertThat(result, contains("John","Tom"));
}

5.3. Predicate.negate()с

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

@Test
public void whenFilterListWithCombinedPredicatesUsingOrAndNegate_thenSuccess(){
    Predicate predicate1 =  str -> str.startsWith("J");
    Predicate predicate2 =  str -> str.length() < 4;

    List result = names.stream()
      .filter(predicate1.or(predicate2.negate()))
      .collect(Collectors.toList());

    assertEquals(3, result.size());
    assertThat(result, contains("Adam","Alexander","John"));
}

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

5.4. ОбъединитьPredicates Inline

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

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

@Test
public void whenFilterListWithCombinedPredicatesInline_thenSuccess(){
    List result = names.stream()
      .filter(((Predicate)name -> name.startsWith("A"))
      .and(name -> name.length()<5))
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

6. Объединение коллекцииPredicates

Наконец,let’s see how to chain a collection of Predicates by reducing them.

В следующем примере у нас естьList изPredicates, которые мы объединили с помощьюPredicate.and():

@Test
public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){
    List> allPredicates = new ArrayList>();
    allPredicates.add(str -> str.startsWith("A"));
    allPredicates.add(str -> str.contains("d"));
    allPredicates.add(str -> str.length() > 4);

    List 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 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(),, строящий комплексPredicates и комбинируяPredicates.

Доступен полный исходный кодover on GitHub.