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

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

Stream<String> s = ...; int emptyStrings = s.filter(String::isEmpty).count(); 

有没有一种方法来创build一个方法的参考是否定一个现有的,即类似的东西:

 int nonEmptyStrings = s.filter(not(String::isEmpty)).count(); 

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

 static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); } 

有一种方法可以组成与当前方法引用相反的方法引用。 请参阅下面的@ 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()比“长时间详细的显式it -> !it.isEmpty()更清晰it -> !it.isEmpty() ,然后否定。

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

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

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

 int notEmpty = 0; for(String s : list) if(!s.isEmpty()) notEmpty++; 

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

**我越来越认为for循环技术更快。 它消除了stream创build,它消除了使用另一个方法调用(谓词负函数),它消除了一个临时累加器列表/计数器。 所以最后一个结构可以使得它更快的一些东西。

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

wish-list:我希望Java Streamfunction有所发展,因为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(); 

Predicate有方法andor negate

但是, String::isEmpty不是一个Predicate ,它只是一个String -> Boolean lambda,它仍然可以成为任何东西,例如Function<String, Boolean>types推断是首先需要发生的事情。 filter方法隐式推断types。 但是如果你在把它作为一个论点来否定之前就不会再发生了。 正如@axtavt所提到的那样, 明确的推断可以被用作一种丑陋的方式:

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

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


不过,如果你想对lambdatypes推断有一些更深入的理解,我想用一些例子来深入解释一下。 看看这些,并试图找出发生了什么事情:

  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工作得很好,每个推断一个不同的types
  • obj2将一个Predicate转换为Object – 愚蠢但是有效
  • f2在运行时失败 – 你不能将PredicateFunction ,这不再是推理
  • f3的作品 – 你调用谓词的lambda定义的方法test
  • p2不编译 – Integer没有isEmpty方法
  • p3也不能编译 – 没有带Integer参数的String::isEmpty方法

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

build立在他人的答案和个人经验:

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

另一种select是在非歧义上下文中将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#negate你正在寻找什么?

您可以使用Eclipse集合中的 Predicates

 MutableList<String> strings = Lists.mutable.empty(); int nonEmptyStrings = strings.count(Predicates.not(String::isEmpty)); 

如果你不能从List更改string:

 List<String> strings = new ArrayList<>(); int nonEmptyStrings = ListAdapter.adapt(strings).count(Predicates.not(String::isEmpty)); 

如果你只需要否定String.isEmpty()你也可以使用StringPredicates.notEmpty()

注意:我是Eclipse Collections的贡献者。

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

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

我已经写了一个完整的实用类(由Askar的提议启发),可以将Java 8 lambdaexpression式转换为java.util.functionjava.util.function定义的任何types化的标准Java 8 lambda。 你可以做例如:

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

因为如果所有的静态方法都被命名as() ,那么将会有很多含糊的地方,所以我select调用方法“as”,接着是返回的types。 这使我们完全控制了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... }