在Java 8中应用一系列过滤器是否有优雅的方法?

时间:2016-09-16 19:10:02

标签: java lambda java-8 java-stream reduce

有没有办法使用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。如果上面的代码可以写成一行,我很好奇吗?

(PS为了清楚起见,这是一个关于Java 8如何工作的问题,而不是关于样式的问题。我确实认为单行方法几乎不像循环一样可读。)

3 个答案:

答案 0 :(得分:6)

我相信这是你正在寻找的:

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
}

<强>更新

合并其他两个答案,您还可以创建一个帮助方法来组合过滤器数组。您可以在帮助程序类中使其成为一个很好的实用程序方法,以便在代码中的任何位置重用,以及覆盖其他谓词(BiPredicateDoublePredicateIntPredicate和{{1} })。

Predicate

答案 1 :(得分:5)

使用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);
}

答案 2 :(得分:3)

要继续@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)));
}