使用 Predicate 或函数作为 Java 流过滤器有什么区别?
What is the difference between using a Predicate or a function as a Java stream filter?
所以假设我在流上使用一些随机过滤器,最直接的方法是直接输入谓词:
x.stream().filter(e -> e % 2 == 0)
我也可以简单的做个引用,提前定义Predicate:
Predicate<Integer> isEven = e -> e % 2 == 0;
...
x.stream().filter(isEven)
不过我也可以使用函数:
private static boolean isEven(Integer integer) {
return integer % 2 == 0;
}
...
x.stream().filter(MyClass::isEven)
据我所知,Predicate 当然有更多限制,而函数可能有副作用等。但是由于像 Venkat Subramaniam 这样的人使用后一种解决方案,我真的很想知道:这里的主要区别是什么?
没有!与方法引用相比,谓词并没有真正的限制!其实这些东西都是一样的!
看看 filter()
函数签名:
filter(Predicate<? super T> predicate)
让我们考虑一下您的示例:
x.stream().filter(e -> e % 2 == 0)
Predicate<Integer> isEven = e -> e % 2 == 0;
...
x.stream().filter(isEven)
第一个只是后者的内联版本。
private static boolean isEven(Integer integer) {
return integer % 2 == 0;
}
...
x.stream().filter(MyClass::isEven)
在这里您可以看到 Method References
的实际效果。 MR 只是一种语法糖,允许您根据现有函数定义 Lambda 表达式。
最终,所有这些表达式都变成了 Predicate 功能接口的相同实现。
此外,您还可以通过使用右侧的块语法在 Lambda 表达式中执行副作用,但通常不建议这样做:
e -> {
//side effects here
return e % 2 == 0;
}
从构建可重用谓词库的角度来看,return 布尔函数库比静态最终谓词实例库更通用的谓词集合。为什么?
考虑一个包含以下内容的库:
public static boolean isEven(int i) { return i -> i % 2 == 0; }
对比
public static final Predicate<Integer> IS_EVEN = i -> i % 2 == 0;
- 如果库函数命名得当,它们读起来会更好。在久违后重新访问代码时,扫描
filter(MyLib::isEven)
比扫描 filter(i -> i % 2 == 0)
更容易,并且 filter(MyLib::isEven)
会准确告诉您正在调用什么,而 filter(MyLib.IS_EVEN)
不会
- 如果您只是想调用您的库函数而不是将其用作谓词,那么它的可读性更高。
MyLib.isEven(i)
扫描效果优于 MyLib.IS_EVEN.test(i)
- 如果您需要使用
IntPredicate
而不是 Predicate<Integer>
Guava Predicate<Integer>
、Apache Collections4 Predicate<Integer>
等,您只需继续使用库函数做 MyLib::isEven
。对于 static final Predicate<Integer>
实例,您必须通过执行 MyLib.IS_EVEN::test
来转换它(无论如何您最终都会使用方法引用)
相同的推理适用于所有函数类型。编写函数。它们可以应用于任何与签名匹配的简单方法引用的功能类型。
两者都将 return 过滤流。使用 predicate(function that return true or false) 更具可读性……这是上面使用 Function 的简写。
public class Demo {
public static Function<Integer, Predicate<Integer>> isGreaterThanNumberIamAboutToApply = pivot -> number -> number % pivot == 0;
public static void main(String[] args) {
List<Integer> myInts = Arrays.asList(new Integer[]{1, 2, 3, 5, 6, 7, 7})
.stream()
.filter(isGreaterThanNumberIamAboutToApply
.apply(3)).collect(Collectors.toList());
myInts.forEach(x -> System.out.printf("%s\n", x));
}
}
所以假设我在流上使用一些随机过滤器,最直接的方法是直接输入谓词:
x.stream().filter(e -> e % 2 == 0)
我也可以简单的做个引用,提前定义Predicate:
Predicate<Integer> isEven = e -> e % 2 == 0;
...
x.stream().filter(isEven)
不过我也可以使用函数:
private static boolean isEven(Integer integer) {
return integer % 2 == 0;
}
...
x.stream().filter(MyClass::isEven)
据我所知,Predicate 当然有更多限制,而函数可能有副作用等。但是由于像 Venkat Subramaniam 这样的人使用后一种解决方案,我真的很想知道:这里的主要区别是什么?
没有!与方法引用相比,谓词并没有真正的限制!其实这些东西都是一样的!
看看 filter()
函数签名:
filter(Predicate<? super T> predicate)
让我们考虑一下您的示例:
x.stream().filter(e -> e % 2 == 0)
Predicate<Integer> isEven = e -> e % 2 == 0;
...
x.stream().filter(isEven)
第一个只是后者的内联版本。
private static boolean isEven(Integer integer) {
return integer % 2 == 0;
}
...
x.stream().filter(MyClass::isEven)
在这里您可以看到 Method References
的实际效果。 MR 只是一种语法糖,允许您根据现有函数定义 Lambda 表达式。
最终,所有这些表达式都变成了 Predicate 功能接口的相同实现。
此外,您还可以通过使用右侧的块语法在 Lambda 表达式中执行副作用,但通常不建议这样做:
e -> {
//side effects here
return e % 2 == 0;
}
从构建可重用谓词库的角度来看,return 布尔函数库比静态最终谓词实例库更通用的谓词集合。为什么?
考虑一个包含以下内容的库:
public static boolean isEven(int i) { return i -> i % 2 == 0; }
对比
public static final Predicate<Integer> IS_EVEN = i -> i % 2 == 0;
- 如果库函数命名得当,它们读起来会更好。在久违后重新访问代码时,扫描
filter(MyLib::isEven)
比扫描filter(i -> i % 2 == 0)
更容易,并且filter(MyLib::isEven)
会准确告诉您正在调用什么,而filter(MyLib.IS_EVEN)
不会 - 如果您只是想调用您的库函数而不是将其用作谓词,那么它的可读性更高。
MyLib.isEven(i)
扫描效果优于MyLib.IS_EVEN.test(i)
- 如果您需要使用
IntPredicate
而不是Predicate<Integer>
GuavaPredicate<Integer>
、Apache Collections4Predicate<Integer>
等,您只需继续使用库函数做MyLib::isEven
。对于static final Predicate<Integer>
实例,您必须通过执行MyLib.IS_EVEN::test
来转换它(无论如何您最终都会使用方法引用)
相同的推理适用于所有函数类型。编写函数。它们可以应用于任何与签名匹配的简单方法引用的功能类型。
两者都将 return 过滤流。使用 predicate(function that return true or false) 更具可读性……这是上面使用 Function 的简写。
public class Demo {
public static Function<Integer, Predicate<Integer>> isGreaterThanNumberIamAboutToApply = pivot -> number -> number % pivot == 0;
public static void main(String[] args) {
List<Integer> myInts = Arrays.asList(new Integer[]{1, 2, 3, 5, 6, 7, 7})
.stream()
.filter(isGreaterThanNumberIamAboutToApply
.apply(3)).collect(Collectors.toList());
myInts.forEach(x -> System.out.printf("%s\n", x));
}
}