Статьи

Отложенное выполнение с предикатом Java

В предыдущих статьях « Отложенное выполнение с поставщиком Java » и « Отложенное выполнение с потребителем Java » я рассматривал легко откладываемое выполнение в Java через стандартные API-интерфейсы Java, которые принимают, соответственно, Поставщика и Потребителя . В этой статье я аналогично расскажу о том, как стандартные API, предоставляемые JDK, обеспечивают отложенное выполнение через стандартный функциональный интерфейс Predicate . Predicate описан в его Javadoc , «Представляет предикат (булевозначная функция) одного аргумента». Другими словами, Predicate похож на функцию, предоставляемую JDK, но его возвращаемое значение ограничено либо true либо false .

Возможно, наиболее распространенное применение Predicate в стандартных API Java в контексте фильтров . Несколько примеров в этом посте будут демонстрировать использование Predicate в сочетании с методами фильтрации для экземпляров Optional и для экземпляров Stream .

Optional.filter (предикат)

Поведение метода фильтра (предиката) необязательного класса описывается таким образом в его документации Javadoc: «Если значение присутствует и значение соответствует данному предикату, возвращает Optional описывающий значение, в противном случае возвращается пустой Optional . » Другими словами, Optional.filter(Predicate) возвращает Optional который будет пустым, если исходный Optional был пустым или если Predicate примененный к оригинальному и существующему Optional преобразуется в false . В противном случае, если исходный Optional имеет «текущее» значение, а Predicate примененный к этому значению, возвращает значение « true , возвращаемый Optional также будет иметь такое же «настоящее» значение. Это показано в следующем листинге кода (полный исходный код доступен на GitHub ).

Optional.filter (Predicate) продемонстрированный

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * Demonstrate use of {@code Optional.filter(Predicate)} on an
 * {@code Optional<Boolean>}.
 */
public static void demonstrateOptionalFilterOnBoolean()
{
   out.print("\nfalse: ");
   getOptionalBoolean(false).filter(b -> b).ifPresent(out::print);
   out.print("\ntrue:  ");
   getOptionalBoolean(true).filter(b -> b).ifPresent(out::print);
   out.print("\nnull:  ");
   getOptionalBoolean(null).filter(b -> b).ifPresent(out::print);
}
 
/**
 * Demonstrate use of {@code Optional.filter(Predicate)} on an
 * {@code Optional<Float>}.
 */
public static void demonstrateOptionalFilterOnFloat()
{
   out.print("\n3.14: ");
   getOptionalFloat(3.14f).filter(f -> f > 0.0).ifPresent(out::print);
   out.print("\n-2.5: ");
   getOptionalFloat(-2.5f).filter(f -> f > 0.0).ifPresent(out::print);
   out.print("\nnull: ");
   getOptionalFloat(null).filter(f -> f > 0.0).ifPresent(out::print);
}

Два метода в приведенном выше листинге кода демонстрируют использование Optional.filter(Predicate) для лямбда-выражения, которое приводит к прямому boolean результату, и к лямбда-выражению, которое приводит к boolean результату, основанному на численном сравнении. В одном случае Predicate является boolean а в другом случае Predicate является числовым сравнением.

Stream.filter (предикат)

Фильтр методов интерфейса Stream (Predicate) работает аналогично методу Optional класса с тем же именем . Следующий листинг кода демонстрирует применение Stream.filter(Predicate) .

Stream.filter (предикат) продемонстрированный

01
02
03
04
05
06
07
08
09
10
/**
 * Demonstrates use of {@code Stream.filter(Predicate}}.
 */
public static void demonstrateStreamFilter()
{
   final int maximum = 100;
   out.println("\nThe probable prime numbers between 1 and " + maximum + " are:");
   final Stream<BigInteger> bigIntegers = getConsecutiveBigIntegers(maximum);
   bigIntegers.filter(bi -> bi.isProbablePrime(100)).forEach(pp -> out.println(" " + pp));
}

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

Для моей следующей иллюстрации Stream.filter(Predicate) я использую удобный метод класса Pattern asPredicate (), чтобы предоставить экземпляр Predicate для обоих примеров с использованием Stream.filter(Predicate) .

Stream.filter (предикат) с демонстрацией Pattern.asPredicate ()

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * Demonstrates use of {@code Pattern.asPredicate()} to provide
 * a {@code Predicate} that can be used with {@code Stream.filter()}.
 */
public static void demonstratePatternAsPredicateInFilter()
{
   final long count
      = getPotentialTelephoneNumbers().stream()
         .filter(PATTERN.asPredicate())
         .peek(out::println)
         .count();
   out.println(count + " valid telephone numbers.");
}

Collection.removeIf (предикат)

Интерфейс Collection определяет (и реализует как метод по умолчанию ) полезный метод removeIf (Predicate) . Существует также несколько реализаций Collection которые реализуют свои собственные переопределенные версии removeIf(Predicate) которые включают ArrayDeque.removeIf (Predicate) , ArrayList.removeIf (Predicate) и Vector.removeIf (Predicate) .

Следующий листинг кода демонстрирует два примера Collection.removeIf(Predicate) в действии. В первом примере используется метод Predicate.negate () для отрицания ожидаемого шаблона регулярного выражения, чтобы элементы, удаляемые из коллекции, были теми, которые НЕ соответствуют регулярному выражению. Второй пример выполняет аналогичную функциональность, но использует метод, введенный в JDK 11 «not», для выполнения этого отрицания.

Collection.removeIf (предикат) с отрицательным узором Pattern.asPredicate ()

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Demonstrates use of {@code Collection.removeIf(Predicate)}
 * in conjunction with {@code Predicate.negate()}.
 */
public static void demonstrateCollectionRemoveIf()
{
   final Set<String> telephoneNumbers = new HashSet<>(getPotentialTelephoneNumbers());
   telephoneNumbers.removeIf(PATTERN.asPredicate().negate());
   out.println(telephoneNumbers);
}
 
/**
 * Demonstrates use of {@code Collection.removeIf(Predicate)}
 * in conjunction with JDK 11-introduced {@code Predicate.not()}.
 */
public static void demonstrateCollectionRemoveIfWithJdk11Not()
{
   final Set<String> telephoneNumbers = new HashSet<>(getPotentialTelephoneNumbers());
   telephoneNumbers.removeIf(not(PATTERN.asPredicate()));
   out.println(telephoneNumbers);
}

Stream.allMatch (предикат)

Метод allMatch (Predicate) интерфейса Stream возвращает true если каждый отдельный элемент в потоке соответствует предоставленному Predicate . Если хотя бы один элемент не соответствует Predicate , метод возвращает значение false .

Stream.allMatch (предикат) продемонстрировано

01
02
03
04
05
06
07
08
09
10
/**
 * Demonstrate use of {@code Stream.allMatch(Predicate)}.
 */
public static void demonstrateStreamAllMatch()
{
   final Set<String> names = getNames();
   final boolean allNamesSixDigits = names.stream()
      .allMatch(name -> name.length() == 6);
   out.println("Are all names " + names + " six digits? " + allNamesSixDigits);
}

Stream.anyMatch (предикат)

Метод Stream.anyMatch (Predicate) возвращает значение true если хотя бы один из его элементов соответствует Predicate и возвращает значение false если ни один из его элементов не соответствует Predicate .

Stream.anyMatch (предикат) продемонстрировано

01
02
03
04
05
06
07
08
09
10
/**
 * Demonstrate use of {@code Stream.anyMatch(Predicate)}.
 */
public static void demonstrateStreamAnyMatch()
{
   final Set<String> names = getNames();
   final boolean anyNamesSixDigits = names.stream()
      .anyMatch(name -> name.length() == 6);
   out.println("Are any names " + names + " six digits? " + anyNamesSixDigits);
}

Stream.noneMatch (предикат)

Метод Stream.noneMatch (Predicate) возвращает true если ни один элемент в потоке не соответствует Predicate и возвращает false если хотя бы один элемент в потоке DOES соответствует Predicate .

Stream.noneMatch (предикат), продемонстрированный

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * Demonstrate use of {@code Stream.noneMatch(Predicate)}.
 */
public static void demonstrateStreamNoneMatch()
{
   final Set<String> names = getNames();
   final boolean noNamesSixDigits = names.stream()
      .noneMatch(name -> name.length() == 6);
   out.println("Are no names " + names + " six digits? " + noNamesSixDigits);
   final boolean noNamesFourDigits = names.stream()
      .noneMatch(name -> name.length() == 4);
   out.println("Are no names " + names + " four digits? " + noNamesFourDigits);
}

Collectors.partitioningBy (предикат)

Хотя есть еще несколько API JDK, использующих Predicate , я завершу этот пост обсуждением и примером использования Collectors.partitioningBy (Predicate) . Этот интересный метод разделяет все элементы в потоке, на который он вызывается, на две группы с одной группой, связанной с ключом Boolean.TRUE (элементы, которые соответствуют Predicate ), и с одной группой, связанной с ключом Boolean.FALSE (те элементы, которые не соответствовать Predicate ). Следующий листинг кода использует это для разделения целых чисел на четные и нечетные.

Collectors.partitioningBy (Predicate) продемонстрированный

01
02
03
04
05
06
07
08
09
10
11
/**
 * Demonstrate use of {@code Collectors.partitioningBy(Predicate)}.
 */
public static void demonstrateCollectorsPartitioningBy()
{
   final Map<Boolean, List<Integer>> evensAndOdds
      = getConsecutiveIntegers(100)
         .collect(Collectors.partitioningBy(integer -> integer % 2 == 0));
   out.println("Evens: " + evensAndOdds.get(Boolean.TRUE));
   out.println("Odds:  " + evensAndOdds.get(Boolean.FALSE));
}

Я использовал несколько «вспомогательных» методов в приведенных выше примерах кода, которые не показаны в этом посте. Эти «вспомогательные» методы и все примеры, показанные в этом посте, доступны на GitHub .

Стандартный функциональный интерфейс Java Predicate — это специализированная версия встроенного функционального интерфейса Java Function, который, возможно, заслуживает своей собственной специализации, поскольку статус возврата true / false так часто используется для представления условий, в которых определенные функции применяются или не применяются. В этом посте было продемонстрировано несколько случаев в JDK, где Predicate используется для определения того, какие элементы потока применяются, независимо от того, применяется ли Optional элемент, и для разделения элементов потока на те, которые удовлетворяют предикату, и те, которые не соответствуют. Попутно также были продемонстрированы удобные методы, такие как Pattern.asPredicate () и Predicate.not () .

Опубликовано на Java Code Geeks с разрешения Дастина Маркса, партнера нашей программы JCG . Смотрите оригинальную статью здесь: отложенное выполнение с помощью предиката Java

Мнения, высказанные участниками Java Code Geeks, являются их собственными.