如何否定一个方法引用谓词

在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); }
214069 次浏览

Predicate#negate不应该是你要找的吗?

有一种方法可以组合与当前方法引用相反的方法引用。参见下面@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:流与集合的性能

**我因为建议for循环技术更快而受到批评。它消除了流的创建,它消除了使用另一个方法调用(谓词为负函数),并且它消除了临时累加器列表/计数器。最后一个构造保留的一些东西可能会让它更快。

我确实认为它更简单、更好,即使不是更快。如果这项工作需要锤子和钉子,不要带电锯和胶水!我知道你们有些人对此有异议。

愿望清单:我希望看到Java Stream函数的发展,现在Java用户更熟悉它们了。例如,Stream中的'count'方法可以接受Predicate,这样就可以直接这样做:

Stream<String> s = ...;
int notEmptyStrings = s.count(it -> !it.isEmpty());


or


List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());

我计划静态导入以下内容,以允许方法引用内联使用:

public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}

如。

Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

< em > < / em >更新:从Java-11开始,JDK也提供了内置的类似的解决方案

根据别人的回答和个人经验:

Predicate<String> blank = String::isEmpty;
content.stream()
.filter(blank.negate())

另一种选择是在明确的上下文中使用lambda强制转换为一个类:

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有方法andornegate

然而,String::isEmpty不是Predicate,它只是一个String -> Boolean lambda,它仍然可以变成任何东西,例如Function<String, Boolean>类型推断是需要首先发生的。filter方法推断类型隐式地。但如果你在传递它作为参数之前对它求反,它就不再发生了。正如@axtavt提到的,显式的推理可以被用作一种丑陋的方式:

s.filter(((Predicate<String>) String::isEmpty).negate()).count()

还有其他的方法在其他答案中建议,静态not方法和lambda最有可能是最好的想法。这就结束了tl;dr部分。


但是,如果您想更深入地理解lambda类型推断,我想通过示例更深入地解释它。看看这些,看看会发生什么:

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;
  • Obj1不编译- lambdas需要推断一个函数接口(=使用一个抽象方法)
  • P1和f1工作得很好,每个都推断出不同的类型
  • obj2将Predicate强制转换为Object -愚蠢但有效
  • f2在运行时失败——你不能将Predicate转换为Function,这不再是推理
  • f3工作-您调用谓词的方法test,该方法由其lambda定义
  • p2不能编译——Integer没有isEmpty方法
  • p3也不能编译——没有带有Integer参数的String::isEmpty静态方法

我希望这能帮助您更深入地了解类型推断的工作原理。

我已经写了一个完整的实用程序类(受到Askar的建议的启发),可以接受Java 8 lambda表达式,并将它们(如果适用)转换为包java.util.function中定义的任何类型的标准Java 8 lambda。你可以这样做:

  • asPredicate(String::isEmpty).negate()
  • asBiPredicate(String::equals).negate()

因为如果所有的静态方法都只命名为as(),会有很多歧义,所以我选择调用方法“as”,后面跟着返回类型。这使我们能够完全控制lambda解释。下面是实用工具类的第一部分(有点大),揭示了所使用的模式。

看一下在这里完成课程(在主旨)。

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

你可以从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 Collections的贡献者。

在这种情况下,你可以使用__abc0和做

int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();

Predicate.not( … )

提供了一个新方法谓词#不

所以你可以对reference方法求反:

Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();

如果你正在使用Spring Boot(2.0.0+),你可以使用:

import org.springframework.util.StringUtils;


...
.filter(StringUtils::hasLength)
...
< p >: return (str != null && !str.isEmpty()); < / p >

因此,它将对isEmpty具有所需的否定效果

你可以通过emptyStrings = s.filter(s->!s.isEmpty()).count();来实现

提示:要对collection.stream().anyMatch(...)求反,可以使用collection.stream().noneMatch(...)