在 Java 8 中是否有一种优雅的方法来应用过滤器数组?
Is there an elegant way of applying an array of filters in Java 8?
有没有办法使用 reduce
或类似的方法将过滤器数组应用于对象数组?
现在,我有这个:
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
LongStream stream = Arrays.stream(initialData);
for (LongPredicate filter : filters) {
stream = stream.filter(filter);
}
long[] dataToUse = stream.toArray();
// Use filtered data
}
更优雅的解决方案如下所示:
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
long[] dataToUse = Arrays.stream(filters).reduce(Arrays.stream(initialData), (a, b) -> a.filter(b)).toArray();
// Use filtered data here
}
当然,上面的示例无法编译,因为 reduce
的两个参数都需要是 LongPredicate
,而不是 LongStream
。很好奇上面的代码能不能写成一行?
(P.S。需要明确的是,这是 Java 8 如何工作的问题,而不是关于样式的问题。我同意单行方法是可读性不如循环。)
我相信这就是您要找的:
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
long[] dataToUse = Arrays.stream(initialData)
.filter(v -> Arrays.stream(filters)
.allMatch(f -> f.test(v)))
.toArray();
// Use filtered data
}
更新
合并其他两个答案,您还可以创建一个辅助方法来组合过滤器数组。您可以在帮助程序 class 中将其作为一个很好的实用方法,以便在您的代码中的任何地方重用,以及覆盖其他谓词(BiPredicate
、DoublePredicate
、IntPredicate
和Predicate
).
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
long[] dataToUse = Arrays.stream(initialData)
.filter(combine(filters))
.toArray();
// Use filtered data
}
public static LongPredicate combine(LongPredicate... filters) {
return Arrays.stream(filters)
.reduce(LongPredicate::and)
.orElse(x -> true);
}
使用 reduce
的解决方案是使用 LongPredicate::and
将您的谓词减少为单个谓词,如下所示:
//prints 6
public static void main(String[] args) {
applyManyFilters(new long[]{1, 2, 3, 4, 5, 6}, l -> l % 2 == 0, l -> l % 3 == 0);
}
private static void applyManyFilters(long[] initialData, LongPredicate... filters) {
Arrays.stream(initialData).filter(
Arrays.stream(filters).reduce(l -> true, LongPredicate::and)
).forEach(System.out::println);
}
要继续回答@Andreas 发布的问题,您可以将其设为静态泛型函数,以使其更易于重用:
public static <T> Stream<T> applyManyFilters(Stream<T> data, Predicate<T>... filters) {
return data.filter(d -> Arrays.stream(filters).allMatch(f -> f.test(d)));
}
有没有办法使用 reduce
或类似的方法将过滤器数组应用于对象数组?
现在,我有这个:
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
LongStream stream = Arrays.stream(initialData);
for (LongPredicate filter : filters) {
stream = stream.filter(filter);
}
long[] dataToUse = stream.toArray();
// Use filtered data
}
更优雅的解决方案如下所示:
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
long[] dataToUse = Arrays.stream(filters).reduce(Arrays.stream(initialData), (a, b) -> a.filter(b)).toArray();
// Use filtered data here
}
当然,上面的示例无法编译,因为 reduce
的两个参数都需要是 LongPredicate
,而不是 LongStream
。很好奇上面的代码能不能写成一行?
(P.S。需要明确的是,这是 Java 8 如何工作的问题,而不是关于样式的问题。我同意单行方法是可读性不如循环。)
我相信这就是您要找的:
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
long[] dataToUse = Arrays.stream(initialData)
.filter(v -> Arrays.stream(filters)
.allMatch(f -> f.test(v)))
.toArray();
// Use filtered data
}
更新
合并其他两个答案,您还可以创建一个辅助方法来组合过滤器数组。您可以在帮助程序 class 中将其作为一个很好的实用方法,以便在您的代码中的任何地方重用,以及覆盖其他谓词(BiPredicate
、DoublePredicate
、IntPredicate
和Predicate
).
private void applyManyFilters(long[] initialData, LongPredicate... filters) {
long[] dataToUse = Arrays.stream(initialData)
.filter(combine(filters))
.toArray();
// Use filtered data
}
public static LongPredicate combine(LongPredicate... filters) {
return Arrays.stream(filters)
.reduce(LongPredicate::and)
.orElse(x -> true);
}
使用 reduce
的解决方案是使用 LongPredicate::and
将您的谓词减少为单个谓词,如下所示:
//prints 6
public static void main(String[] args) {
applyManyFilters(new long[]{1, 2, 3, 4, 5, 6}, l -> l % 2 == 0, l -> l % 3 == 0);
}
private static void applyManyFilters(long[] initialData, LongPredicate... filters) {
Arrays.stream(initialData).filter(
Arrays.stream(filters).reduce(l -> true, LongPredicate::and)
).forEach(System.out::println);
}
要继续回答@Andreas 发布的问题,您可以将其设为静态泛型函数,以使其更易于重用:
public static <T> Stream<T> applyManyFilters(Stream<T> data, Predicate<T>... filters) {
return data.filter(d -> Arrays.stream(filters).allMatch(f -> f.test(d)));
}