在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); }
java-11提供了一种新方法Predicate#not
所以你可以否定方法参考:
Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();
我编写了一个完整的实用程序类(受Askar提议的启发),它可以使用Java 8 lambda表达式并将它们(如果适用)转换为java.util.function
包中定义的任何类型化标准Java 8 lambda。你可以这样做:
asPredicate(String::isEmpty).negate()
asBiPredicate(String::equals).negate()
因为如果将所有静态方法命名为as()
会有很多歧义,我选择将方法称为“as”,后跟返回的类型。这使我们可以完全控制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...
}
如果你使用的是Spring Boot(2.0.0+),你可以使用:
import org.springframework.util.StringUtils;
...
.filter(StringUtils::hasLength)
...
哪个:return (str != null && !str.isEmpty());
因此它将对isEmpty
产生所需的否定效果
你可以做到这一点长emptyStrings = s.filter(s->!s.isEmpty()).count();
我打算静态导入以下内容以允许方法引用内联使用:
public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}
EG
Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
更新: - JDK / 11也可能提供similar solution。
有一种方法可以组成一个与当前方法引用相反的方法引用。请参阅下面的@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());
Predicate
有方法and
,or
和negate
。
然而,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;
Predicate
施放了Object
- 愚蠢但有效Predicate
强制转换为Function
,它不再是推论test
Integer
没有isEmpty
方法String::isEmpty
静态方法与Integer
参数我希望这有助于更深入地了解类型推断的工作原理。
基于其他人的答案和个人经验:
Predicate<String> blank = String::isEmpty;
content.stream()
.filter(blank.negate())
另一个选择是在非模糊上下文中将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
不应该是你想要的吗?
在这种情况下,你可以使用org.apache.commons.lang3.StringUtils
and做
int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();
你可以使用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的贡献者。