如何否定方法引用谓词

时间:2014-01-31 19:04:08

标签: java predicate java-8 negate

在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); }

12 个答案:

答案 0 :(得分:183)

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

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

e.g。

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

更新 : - JDK / 11也可能提供similar solution

答案 1 :(得分:136)

有一种方法可以编写与当前方法引用相反的方法引用。请参阅下面的@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: performance of Streams vs Collections

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

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

wish-list:我希望看到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());

答案 2 :(得分:82)

提供了一种新方法Predicate#not

因此您可以否定方法引用:

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

答案 3 :(得分:80)

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有效 - 你调用由lambda定义的谓词的方法test
  • p2无法编译 - Integer没有isEmpty方法
  • p3也没有编译 - 没有带String::isEmpty参数的Integer静态方法

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

答案 4 :(得分:43)

以其他人的答案和个人经验为基础:

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

答案 5 :(得分: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;
    }

}

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

stream.filter(as(String::isEmpty).negate())

答案 6 :(得分:11)

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

答案 7 :(得分:8)

在这种情况下,您可以使用org.apache.commons.lang3.StringUtils并执行

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

答案 8 :(得分:4)

您可以使用Predicates

中的Eclipse Collections
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的撰稿人。

答案 9 :(得分:3)

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

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

因为如果将所有静态方法命名为as()会有很多歧义,我选择调用方法&#34; as&#34;然后是返回的类型。这使我们可以完全控制lambda解释。下面是(有点大)实用程序类的第一部分,揭示了所使用的模式。

查看complete class here(在要点)。

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

答案 10 :(得分:0)

如果您使用的是Spring Boot(2.0.0+),则可以使用:

import org.springframework.util.StringUtils;

...
.filter(StringUtils::hasLength)
...

这样做: return (str != null && !str.isEmpty());

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

答案 11 :(得分:0)

只要emptyStrings = s.filter(s->!s.isEmpty()).count();