如何否定方法引用谓词

ass*_*ias 301 java predicate negate java-8

在Java 8中,您可以使用方法引用来过滤流,例如:

Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();
Run Code Online (Sandbox Code Playgroud)

有没有办法创建一个方法引用,它是对现有方法的否定,即:

long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Run Code Online (Sandbox Code Playgroud)

我可以创建not如下所示的方法,但我想知道JDK是否提供了类似的东西.

static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }
Run Code Online (Sandbox Code Playgroud)

小智 199

我打算静态导入以下内容以允许方法引用内联使用:

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}
Run Code Online (Sandbox Code Playgroud)

例如

Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Run Code Online (Sandbox Code Playgroud)

更新: - JDK/11也可能提供类似的解决方案.

  • 它位于番石榴http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Predicates.html#not(com.google.common.base.Predicate) (20认同)
  • @SaintHill然后你必须把它写出来,给参数一个名字 (9认同)
  • 更新了Guava链接:https://static.javadoc.io/com.google.guava/guava/23.0/com/google/common/base/Predicates.html#not-com.google.common.base.Predicate- (5认同)

The*_*tor 144

有一种方法可以组成一个与当前方法引用相反的方法引用.请参阅下面的@vlasec的答案,该答案显示了如何通过将方法引用显式地转换为a Predicate然后使用该negate函数进行转换.这是其他一些不太麻烦的方法之一.

与此相反:

Stream<String> s = ...;
int emptyStrings = s.filter(String::isEmpty).count();
Run Code Online (Sandbox Code Playgroud)

这是:

Stream<String> s = ...;
int notEmptyStrings = s.filter(((Predicate<String>) String::isEmpty).negate()).count()
Run Code Online (Sandbox Code Playgroud)

或这个:

Stream<String> s = ...;
int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count();
Run Code Online (Sandbox Code Playgroud)

就个人而言,我更喜欢后来的技术,因为我发现它it -> !it.isEmpty()比一个冗长的详细显式演员更容易阅读然后否定.

也可以创建一个谓词并重用它:

Predicate<String> notEmpty = (String it) -> !it.isEmpty();

Stream<String> s = ...;
int notEmptyStrings = s.filter(notEmpty).count();
Run Code Online (Sandbox Code Playgroud)

或者,如果有一个集合或数组,只需使用简单的for循环,开销较少,*可能**更快:

int notEmpty = 0;
for(String s : list) if(!s.isEmpty()) notEmpty++;
Run Code Online (Sandbox Code Playgroud)

*如果你想知道什么是更快的,那么使用JMH http://openjdk.java.net/projects/code-tools/jmh,并避免使用手工基准代码,除非它避免所有JVM优化 - 参见Java 8:Streams的性能vs收藏

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

我认为它更简单,更好,即使不是更快.如果工作需要锤子和钉子,请不要带电锯和胶水!我知道有些人对此持有异议.

wish-list:我希望看到Java Stream函数现在有点发展,Java用户对它们更熟悉.例如,Stream中的'count'方法可以接受a Predicate,这样可以直接完成,如下所示:

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

or

List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());
Run Code Online (Sandbox Code Playgroud)

  • 谈到性能,不要假设太多! (5认同)
  • negate()似乎是一个理想的解决方案.遗憾的是它不像`Predicate.negate(String :: isEmpty)那样是静态的;`没有繁琐的演员. (4认同)
  • 我要求您详细说明“运行速度要快得多”。问题:(1)它是更快还是“快得多”?(2) 如果是,为什么?你确定了什么?最好由您作为声明的作者回答。我不认为它更快或更慢。谢谢 (3认同)
  • 然后我会把它抛出来供你考虑 - 它消除了一个流创建,它消除了使用另一个方法调用(谓词的负函数),并且它消除了一个临时的累加器列表/计数器.所以最后一个构造保存了一些东西.我不确定它是否更快或更快,但我认为它"更快".但也许"很多"是主观的.编写后期编码比使负谓词和流进行直接编码更简单.我的偏好. (2认同)

Ant*_*iuc 131

提供了一种新方法Predicate#not

所以你可以否定方法参考:

Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();
Run Code Online (Sandbox Code Playgroud)

  • 要在不使用谓词的情况下使用“not(String::isEmpty)”,请通过“import static java.util.function.Predicate.not;”导入它 (2认同)

Vla*_*sec 85

Predicate有方法and,ornegate.

然而,String::isEmpty不是Predicate,它只是一个String -> Booleanlambda,它仍然可以成为任何东西,例如Function<String, Boolean>.类型推断是首先需要发生的事情.该filter方法隐式推断类型.但如果你在将它作为一个论点传递之前否定它,它就不再发生了.正如@axtavt所提到的,显式推理可以用作一种丑陋的方式:

s.filter(((Predicate<String>) String::isEmpty).negate()).count()
Run Code Online (Sandbox Code Playgroud)

在其他答案中还有其他建议,静态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;
Run Code Online (Sandbox Code Playgroud)
  • obj1不编译 - lambdas需要推断一个功能接口(=用一个抽象方法)
  • p1和f1工作得很好,每个都推断出不同的类型
  • OBJ2蒙上了PredicateObject-愚蠢,但有效
  • f2在运行时失败 - 你无法Predicate转换Function,它不再是推理
  • f3有效 - 你调用test由lambda定义的谓词方法
  • p2不编译 - Integer没有isEmpty方法
  • p3也没有编译 - 没有String::isEmptyInteger参数的静态方法

我希望这有助于更深入地了解类型推理是如何工作的.


Jos*_*ban 44

基于其他人的答案和个人经验:

Predicate<String> blank = String::isEmpty;
content.stream()
       .filter(blank.negate())
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是 - 你不能像想象中那样内联函数式`::`引用(`String :: isEmpty.negate()`),但是如果你首先赋值给一个变量(或者首先转换为`Predicate <String>` ), 这样可行.我认为lambda w /`!`在大多数情况下最具可读性,但知道什么可以编译和不编译是有帮助的. (4认同)
  • @JoshuaGoldberg我在答案中解释说:方法引用本身并不是谓词.这里,铸造是由变量完成的. (2认同)

Ask*_*kov 16

另一个选择是在非模糊上下文中将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;
    }

}
Run Code Online (Sandbox Code Playgroud)

...然后静态导入实用程序类:

stream.filter(as(String::isEmpty).negate())
Run Code Online (Sandbox Code Playgroud)


Mar*_*o13 11

Predicate#negate应该是你在找什么?

  • 你必须先将`String :: isEmpty()`转换为`Predicate <String>` - 它非常难看. (20认同)
  • @SotiriosDelimanolis我知道,但这就失去了目的 - 在那种情况下我宁愿写`s - >!s.isEmpty()`! (7认同)
  • @assylias用作`Predicate <String> p =(Predicate <String>)String :: isEmpty;`和`p.negate()`. (3认同)

out*_*nds 8

在这种情况下,你可以使用org.apache.commons.lang3.StringUtils和做

int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();
Run Code Online (Sandbox Code Playgroud)

  • 不.问题是如何否定任何方法引用,并以`String :: isEmpty`为例.如果您有此用例,它仍然是相关信息,但如果它只回答String用例,则不应接受它. (6认同)