418

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

12 回答 12

323

Predicate.not( … )

提供了一个新方法Predicate#not

所以你可以否定方法引用:

Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();
于 2018-07-20T09:58:54.117 回答
237

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

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

例如

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

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

于 2014-03-18T22:26:08.990 回答
158

有一种方法可以组成与当前方法引用相反的方法引用。请参阅下面的@vlasec 的答案,该答案显示了如何将方法引用显式转换为 a 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:Streams 的性能与收藏

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

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

愿望清单:我希望看到 JavaStream用户对 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());
于 2014-02-02T04:05:21.297 回答
98

Predicate有方法and和。ornegate

但是,String::isEmpty它不是一个Predicate,它只是一个String -> Booleanlambda 并且它仍然可以变成任何东西,例如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 强制转换PredicateObject- 愚蠢但有效
  • f2 在运行时失败 - 你不能Predicate转换为Function,它不再是关于推理
  • f3 有效 - 您调用test由其 lambda 定义的谓词方法
  • p2 不编译 -Integer没有isEmpty方法
  • p3 也不编译 - 没有带参数的String::isEmpty静态方法Integer

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

于 2015-05-28T12:26:40.630 回答
51

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

Predicate<String> blank = String::isEmpty;
content.stream()
       .filter(blank.negate())
于 2015-04-01T21:37:06.523 回答
18

另一种选择是在非模棱两可的上下文中使用 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())
于 2015-05-27T07:03:22.823 回答
12

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

于 2014-01-31T19:14:57.493 回答
10

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

int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();
于 2016-10-20T14:06:52.497 回答
4

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

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

因为如果所有的静态方法都被命名为 just 会有很多歧义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...
}
于 2016-01-28T23:46:50.480 回答
4

您可以使用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 的贡献者。

于 2016-03-27T00:11:36.620 回答
1

你可以做到这一点,只要emptyStrings = s.filter(s->!s.isEmpty()).count();

于 2018-09-28T12:29:36.893 回答
0

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

import org.springframework.util.StringUtils;

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

哪个: return (str != null && !str.isEmpty());

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

于 2018-09-25T10:34:34.383 回答