如何否定一个方法引用谓词
在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 Stream
function有所发展,因为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
有方法and
, or
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在运行时失败 – 你不能将
Predicate
为Function
,这不再是推理 - 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.function
包java.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... }