Ответ 1
Predicate.not( … )
java-11 предлагает новый метод Predicate # not
Таким образом, вы можете отменить ссылку на метод:
Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();
В Java 8 вы можете использовать ссылку на метод для фильтрации потока, например:
Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();
Есть ли способ создать ссылку на метод, который является отрицанием существующего, то есть что-то вроде:
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Я мог бы создать метод not
как показано ниже, но мне было интересно, предлагает ли JDK нечто подобное.
static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }
Predicate.not( … )
java-11 предлагает новый метод Predicate # not
Таким образом, вы можете отменить ссылку на метод:
Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();
Я планирую статический импорт следующего, чтобы позволить использование ссылки на метод inline:
public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}
например
Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Обновление: - Ну, JDK/11 может предлагать аналогичное решение.
Существует способ составить ссылку на метод, которая противоположна текущей ссылке метода. См. Ниже приведенный ниже ответ @vlasec, в котором показано, как явно указывать ссылку метода на Predicate
, а затем преобразовывать его с помощью функции negate
. Это один из немногих других не слишком трудных способов сделать это.
Противоположность этому:
Stream<String> s = ...;
int emptyStrings = s.filter(String::isEmpty).count();
:
Stream<String> s = ...;
int notEmptyStrings = s.filter(((Predicate<String>) String::isEmpty).negate()).count()
или это:
Stream<String> s = ...;
int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count();
Лично я предпочитаю более позднюю технику, потому что я нахожу более понятным читать it -> !it.isEmpty()
, чем длинный подробный явный листинг, а затем отрицать.
Можно также создать предикат и повторно использовать его:
Predicate<String> notEmpty = (String it) -> !it.isEmpty();
Stream<String> s = ...;
int notEmptyStrings = s.filter(notEmpty).count();
Или, если у вас есть коллекция или массив, просто используйте простой цикл for, который имеет меньше накладных расходов и * может быть быстрее:
int notEmpty = 0;
for(String s : list) if(!s.isEmpty()) notEmpty++;
* Если вы хотите узнать, что быстрее, используйте JMH http://openjdk.java.net/projects/code-tools/jmh и избегайте ручного теста, если он не избегает всех оптимизаций JVM - см. Java 8: производительность потоков и коллекций
** Я получаю flak за предположение, что метод for-loop работает быстрее. Он устраняет создание потока, исключает использование другого вызова метода (отрицательная функция для предиката) и исключает временный список/счетчик аккумулятора. Итак, несколько вещей, которые сохраняются последней конструкцией, которая может ускорить ее работу.
Я думаю, что это проще и приятнее, хотя, даже если не быстрее. Если работа требует молота и гвоздя, не приносите бензопилу и клей! Я знаю, что некоторые из вас с этим справляются.
Список пожеланий: Я хотел бы, чтобы функции Java Stream
немного развивались, когда пользователи Java более знакомы с ними. Например, метод "count" в Stream может принять Predicate
, чтобы это можно было сделать следующим образом:
Stream<String> s = ...;
int notEmptyStrings = s.count(it -> !it.isEmpty());
or
List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());
Predicate
имеет методы and
, or
и negate
.
Однако String::isEmpty
не является Predicate
, это просто String → Boolean
лямбда- String → Boolean
и она все равно может стать чем угодно, например, Function<String, Boolean>
. Вывод типа - это то, что должно произойти в первую очередь. Метод filter
выводит тип неявно. Но если вы отрицаете это, прежде чем передать в качестве аргумента, это больше не происходит. Как упомянуто @axtavt, явный вывод можно использовать как уродливый способ:
s.filter(((Predicate<String>) String::isEmpty).negate()).count()
Есть другие способы, рекомендуемые в других ответах, со статическим not
методом и лямбдами, скорее всего, являются лучшими идеями. На этом мы завершаем раздел tl; dr.
Однако, если вы хотите получить более глубокое понимание вывода типа лямбда, я хотел бы объяснить это немного глубже, используя примеры. Посмотрите на них и попытайтесь выяснить, что происходит:
Object obj1 = String::isEmpty;
Predicate<String> p1 = s -> s.isEmpty();
Function<String, Boolean> f1 = String::isEmpty;
Object obj2 = p1;
Function<String, Boolean> f2 = (Function<String, Boolean>) obj2;
Function<String, Boolean> f3 = p1::test;
Predicate<Integer> p2 = s -> s.isEmpty();
Predicate<Integer> p3 = String::isEmpty;
Object
Predicate
- глупо, но верноPredicate
к Function
, это больше не выводtest
который определяется его лямбда-выражениемInteger
нет метода isEmpty
String::isEmpty
с аргументом Integer
Я надеюсь, что это поможет лучше понять, как работает вывод типа.
Основываясь на других ответах и личном опыте:
Predicate<String> blank = String::isEmpty;
content.stream()
.filter(blank.negate())
Другой вариант - использовать lambda casting в недвусмысленных контекстах в один класс:
public static class Lambdas {
public static <T> Predicate<T> as(Predicate<T> predicate){
return predicate;
}
public static <T> Consumer<T> as(Consumer<T> consumer){
return consumer;
}
public static <T> Supplier<T> as(Supplier<T> supplier){
return supplier;
}
public static <T, R> Function<T, R> as(Function<T, R> function){
return function;
}
}
... и затем статический импорт класса утилиты:
stream.filter(as(String::isEmpty).negate())
Не следует Predicate#negate
быть тем, что вы ищете?
В этом случае вы можете использовать org.apache.commons.lang3.StringUtils
и do
int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();
Вы можете использовать Predicates из Коллекции Eclipse
MutableList<String> strings = Lists.mutable.empty();
int nonEmptyStrings = strings.count(Predicates.not(String::isEmpty));
Если вы не можете изменить строки из List
:
List<String> strings = new ArrayList<>();
int nonEmptyStrings = ListAdapter.adapt(strings).count(Predicates.not(String::isEmpty));
Если вам нужно только отрицание String.isEmpty()
, вы также можете использовать StringPredicates.notEmpty()
.
Примечание. Я участвую в коллекциях Eclipse.
Я написал полный класс утилиты (вдохновленный предложением Аскара), который может принимать выражение lambda Java 8 и превращать их (если применимо) в любую типизированную стандартную Java-лямбда Java 8, определенную в пакете java.util.function
. Например, вы можете:
asPredicate(String::isEmpty).negate()
asBiPredicate(String::equals).negate()
Потому что было бы много неоднозначностей, если бы все статические методы назывались просто as()
, я решил вызвать метод "как", за которым следует возвращаемый тип. Это дает нам полный контроль над лямбда-интерпретацией. Ниже приведена первая часть (несколько большого) класса утилиты, показывающая используемый шаблон.
Посмотрите полный класс здесь (по сути).
public class FunctionCastUtil {
public static <T, U> BiConsumer<T, U> asBiConsumer(BiConsumer<T, U> biConsumer) {
return biConsumer;
}
public static <T, U, R> BiFunction<T, U, R> asBiFunction(BiFunction<T, U, R> biFunction) {
return biFunction;
}
public static <T> BinaryOperator<T> asBinaryOperator(BinaryOperator<T> binaryOperator) {
return binaryOperator;
}
... and so on...
}
Если вы используете Spring Boot (2.0. 0+), вы можете использовать:
import org.springframework.util.StringUtils;
...
.filter(StringUtils::hasLength)
...
Что делает: return (str != null && !str.isEmpty());
Таким образом, он будет иметь необходимый эффект отрицания для isEmpty
Вы можете сделать это как long emptyStrings = s.filter(s->!s.isEmpty()).count();