使用 Java Streams 过滤嵌套列表

Filtering of nested lists with Java Streams

提问人:CurvedHalo 提问时间:3/30/2022 更新时间:3/30/2022 访问量:2271

问:

我有一个可以过滤一些嵌套列表。考虑一个包含 哪个包含 的 。我需要过滤,以返回所有包含至少一个满足给定条件的 B 和至少一个满足给定条件的 C 的 A 对象。List<A>List<B>List<C>

我在下面有一个人为的例子,它基本上是在做我在实际示例中尝试实现的事情。以一个城市的学校列表为例,每所学校都有很多科目,每个科目都有很多老师。我想保留 subject.subjectName = 'Math' 的学校列表,并且该科目中至少有一名教师的 teacher.age > 65。

我已经使用自定义谓词实现了这一点,因此我也在我的示例中创建了一个 this。 我收到一个错误,即“无法从布尔值转换为流”,但老实说,我在这种情况下很挣扎。

    @Getter
    @Setter
    class School {
        private String schoolId;
        private List<Subject> classes;
        
    }
        
    @Getter
    @Setter
    class Subject {
        String subjectName;
        List<Teacher> teachers;
    }
    
    @Getter
    @Setter
    class Teacher {
        private String teacherName;
        private Integer age;
    }

    public class TestClass {
        public static void main( String[] args ){
            List<School> schools;
            
            // Get All schools where A mathTeacher is over retirement age
            List<School> schoolsWithRetirementAgeMathTeachers = schools.stream()
                    .filter(school -> null != school.getClasses())
                    .flatMap(school -> {
                        return school.getClasses().stream()
                                .filter(subject -> subject.getSubjectName().equalsIgnoreCase("Math"))
                                .filter(subject -> null != subject.getTeachers())
                                .flatMap(subject -> {
                                    return subject.getTeachers().stream()
                                            .filter(teacher -> teacher != null)
                                            .anyMatch(isRetirementAge);
                                });
                    }).collect(Collectors.toList());
                    
        }
                
        public static Predicate<Teacher> isRetirementAge = teacher -> teacher.getAge() > 65;
    
    }
Java java-8 java-stream 嵌套列表 谓词

评论

0赞 shmosel 3/30/2022
这是您在要过滤的条件下过滤的,还是它们不相关的条件?CB

答:

3赞 shmosel 3/30/2022 #1

如果尝试返回父对象,则不想使用 .您只需要嵌套一些调用:flatMap()anyMatch()

List<A> filtered = listA.stream()
        .filter(a -> a.getListB()
                .stream()
                .anyMatch(b -> testB(b) && b.getListC()
                        .stream()
                        .anyMatch(c -> testC(c))))
        .collect(Collectors.toList());

评论

0赞 CurvedHalo 3/31/2022
如果我想扩展它,而不是只检查嵌套值的存在,我想过滤掉所有不满足条件的值?也就是说,每个项目都只有一个满足条件的项目。同样,每个都有一个,它只包含那些满足 .. 条件的项目。我该怎么做?它实质上是将嵌套筛选和将筛选器保留在流范围之外AList<B>testB()BList<C>CtestC()
0赞 shmosel 4/1/2022
然后,您需要创建一个每个元素的过滤副本。除非您愿意就地修改列表。map()
0赞 experiment unit 1998X 10/12/2023
如果我希望获得每个元素的过滤副本,但我也不想修改原始副本,这是否意味着我必须创建具有过滤 b 副本和 c 过滤副本的 A 副本?
1赞 shmosel 10/13/2023
@experimentunit1998X 这只会过滤掉顶级元素(基于嵌套内容)。如果要在多个级别创建过滤副本,可以添加一个方法来 like .AA filtered(Predicate<B> p1, Predicate<C> p2) { return new A(bList.stream().filter(p1).map(b -> b.filtered(p2)).toList()); }
0赞 Naman 3/30/2022 #2

由于您正在寻找 s 作为输出,因此您不需要/将流/到另一个对象中。ing 功能必须足够智能,才能遍历嵌套集合并确认是否存在基于您的要求的集合。SchoolmapflatmapfilteranyMatch

主要在下面的代码中,我们过滤了所有那些存在名为“数学”的科目的学校,并且其中一位教授数学的老师超过了上述退休年龄。

// Get All schools where a Math teacher is over retirement age
List<School> schoolsWithRetirementAgeMathTeachers = schools.stream()
        .filter(school -> school.getSubjects()
                .stream()
                .anyMatch(subject ->
                        subject.getName().equalsIgnoreCase("Math") &&
                                subject.getTeachers()
                                        .stream()
                                        .anyMatch(isRetirementAge)
                ))
        .collect(Collectors.toList());

评论

0赞 CurvedHalo 3/31/2022
如果我想扩展它,只保留“主题”列表中名称 = math 的实例,即我想过滤掉这些实例。因为目前它只是用一场比赛返回学校,但下面的所有科目仍然存在
0赞 Naman 4/1/2022
要获得学校的科目,那么您必须流map