如何使用Stream过滤嵌套对象?

时间:2018-09-13 07:42:14

标签: java

我想使用Stream API过滤嵌套对象。问题是嵌套类太多,使用下面的方法,我编写了太多重复的代码。

有没有办法处理没有重复代码的流?

public class Country{
    Map<String, City> cities;
}

public class City{
    Map<String, School> schools;
}

public class School{
    String name;
    String address;
    Model model;
}

public class Model{
    String name;
    Teacher teacher;
}

public class Teacher{
    String name;
    String id;
}

我的流;

country.getCities().values().stream().foreach(
     (City city) ->
         city.getSchools()
             .entrySet()      
             .stream()
             .filter(schoolEntry -> schoolEntry.getValue().getName().equals("test")) 
             .filter(schoolEntry -> schoolEntry.getValue().getModel().getName().equals("test2"))
             .filter(schoolEntry -> schoolEntry.getValue().getModel().getTeacher().getName().equals("test2"))
             .foreach(schoolEntry -> {
                  String schoolKey = schoolEntry.getKey();
                  resultList.put(schoolKey, schoolEntry.getValue().getModel().getTeacher().getId());
              })    
);

3 个答案:

答案 0 :(得分:1)

您可以使用“更大的lambda”:

.filter(schoolEntry -> {
    School value = schoolEntry.getValue();
    return value.getName().equals("test")
        && value.getModel().getName().equals("test2")
        && value.getModel().getTeacher().getName().equals("test2")
}

或者,您也可以在makePredicate类内创建一个School方法,如下所示:

public static Predicate<School> makePredicate(String first, String second) {
    return (school) -> school.name.equals(first) && this.model.getName().equals("test2") && this.model.getTeacher().getName().equals("test2");
}

并将其用作过滤谓词:

.filter(School.makePredicate("test", "test2"))

如果可以找到一个名称,请用更合适的名称替换

答案 1 :(得分:1)

您可以定义一种方法来将其用作Predicate来过滤学校。

public static boolean matches(School school, String schoolName, String modelName, String teacherId) {
    return school.getName().equals(schoolName) 
            && school.getModel().getName().equals(modelName) 
            && school.getModel().getTeacher().getId().equals(teacherId);
}

将此应用于流:

public static Map<String, String> getSchoolAndTeacherFrom(Country country, Predicate<School> schoolWithModelAndTeacher) {
    return country.getCities().values().stream()
            .flatMap(c -> c.getSchools().entrySet().stream())
            .filter(s -> schoolWithModelAndTeacher.test(s.getValue()))
            .collect(Collectors.toMap(Entry::getKey, schoolEntry -> schoolEntry.getValue().getModel().getTeacher().getId()));
}

这样使用:

    Country country = <county>
    Predicate<School> schoolWithModelAndTeacher = school -> matches(school, "test1", "test2", "test2");
    getSchoolAndTeacherFrom(country, schoolWithModelAndTeacher);

一些进一步的想法:

如果地图schools使用School.getName()作为键,那么我们可以这样写:

public static Map<String, String> getSchoolAndTeacherFrom(Country country, Predicate<School> schoolWithModelAndTeacher) {
    return country.getCities().values().stream()
            .flatMap(city -> city.getSchools().values().stream())
            .filter(schoolWithModelAndTeacher::test)
            .collect(Collectors.toMap(School::getName, school -> school.getModel().getTeacher().getId()));
}

假设一个国家/地区的学校名称和老师ID是唯一的(型号名称是常见的),则过滤将得出一个单一值(如果有)。但是,则不需要Map作为结果类型。类型Entry<String String>的结果可以做到。
而且如果谓词的参数仍然是已知的(学校,模型,教师),那么这整件事只是一个问题,在特定国家/地区,给定模型上的学校是否有给定的老师。然后我们可以写得更短:

public static boolean isMatchingSchoolInCountryPresent(Country country, Predicate<School> schoolWithModelAndTeacher) {
    return country.getCities().values().stream()
            .flatMap(c -> c.getSchools().values().stream())
            .anyMatch(schoolWithModelAndTeacher::test);
}

答案 2 :(得分:0)

首先,您需要根据要在流上进行过滤的条件创建一个谓词

Predicate<School> func1 = (school)-> "test".equals(school.name)
        && "test2".equals(school.model.getName())
        && "test2".equals(school.model.getTeacher().getName());

然后您可以通过以下方式轻松实现目标:

country.cities.
        entrySet()
        .stream()
        .map(Map.Entry::getValue)
        .flatMap(x->x.schools.entrySet().stream())
        .filter(s->func1.test(s.getValue()))
        .collect(toMap(Map.Entry::getKey, schoolEntry -> schoolEntry.getValue().getModel().getTeacher().id));