Java 8 Streams - 将固定数量的谓词过滤器应用于单个Stream

4

有没有一种方法可以将固定数量的 Predicates 应用于已打开的 Stream?我真的无法让我的任何尝试都起作用。不管是什么尝试,要么以流关闭错误结束,要么没有应用所有过滤器。

示例:

 // list of all JLabels of any container
private final List<JLabel> listOfLabels = new ArrayList<>();
// set of user-defined filters
private final Set<Predicate<JLabel>> filters = new HashSet<>();

// let's add some filters
filters.add(label -> label.getBackground() == Color.RED);
filters.add(label -> label.getWidth() > 500);

我该如何将所有过滤器应用于listOfFiles的流?假设我们想要隐藏与这些过滤器不匹配的JLabels。我正在寻找类似于下面的非工作代码片段的东西。
public void applyFilters() {
   listOfLabels.forEach(label -> label.setVisible(false)); // hide all labels
   Stream<JLabel> stream = listOfLabels.stream();
   filters.forEach(stream::filter);
   stream.forEach(label -> label.setVisible(true));
   // stream closed error
}

在执行方法applyFilters()之后,容器应该只显示与filters集合中定义的所有谓词匹配的标签(例如,背景为红色且宽度大于500)。请注意保留HTML标记。

3
你需要翻译的内容是:filters.stream().reduce(Predicate::or) (还是 Predicate::and)?这段代码的意思是:通过流式操作将多个 Predicate 合并为一个。Predicate::or 表示使用逻辑“或”将所有谓词组合在一起,而 Predicate::and 则表示使用逻辑“与”将它们组合在一起。 - Andy Turner
3个回答

3
当您使用Stream时,必须始终使用由应用先前操作返回的Stream对象链接。从API中可以看到:
Stream<T> filter(Predicate<? super T> predicate);

返回一个流,其中包含与给定谓词匹配的该流的元素。

考虑到这一点,在您的情况下,我们可以应用以下内容:

转换为:

filters.forEach(stream::filter);

致:

   for (Predicate<JLabel> filter : filters) {
        stream = stream.filter(filter);
   }

这应该可以正常工作,因为您现在更新了到Stream对象的链接并遵循文档。


感谢您提供的答案和解释,但我更喜欢函数式风格并且您提供的代码无法改写为函数式风格。 - glf4k

2

试一下这个。

public void applyFilters() {
    listOfLabels.forEach(label -> label.setVisible(false));
    listOfLabels.stream()
        .filter(label -> filters.stream()
            .allMatch(predicate -> predicate.test(label)))
        .forEach(label -> label.setVisible(true));
}

2

您可以通过 Predicate::and 操作符将所有过滤器缩减为单个过滤器:

Predicate<JLabel> allFilters = filters.stream()
    .reduce(Predicate::and)
    .orElse(t -> true);

然后,使用这个缩小后的过滤器进行筛选:
listOfLabels.forEach(label -> label.setVisible(false));

listOfLabels.stream()
    .filter(allFilters)
    .forEach(label -> label.setVisible(true));

在最后一步中,您可能还想采取稍微不同的方法:

listOfLabels.forEach(label -> label.setVisible(allFilters.test(label)));

这样做的好处是只需对标签进行一次遍历。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接