我想使用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());
})
);
答案 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));