«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.
«