【问题标题】:Comparator.comparing(...) of a nested field嵌套字段的 Comparator.comparing(...)
【发布时间】:2016-11-26 18:20:32
【问题描述】:

假设我有一个这样的领域模型:

class Lecture {
     Course course;
     ... // getters
}

class Course {
     Teacher teacher;
     int studentSize;
     ... // getters
}

class Teacher {
     int age;
     ... // getters
}

现在我可以像这样创建一个教师比较器:

    return Comparator
            .comparing(Teacher::getAge);

但是我如何比较 Lecture 的嵌套字段,像这样?

    return Comparator
            .comparing(Lecture::getCourse::getTeacher:getAge) 
            .thenComparing(Lecture::getCourse::getStudentSize);

我无法在模型上添加方法Lecture.getTeacherAge()

【问题讨论】:

  • 为什么不使用 lambda?
  • 啊……那一刻我意识到我问了一个愚蠢的问题:)(并不是说有任何愚蠢的问题。)

标签: java lambda java-8 comparator


【解决方案1】:

您不能嵌套方法引用。您可以改用 lambda 表达式:

return Comparator
        .comparing(l->l.getCourse().getTeacher().getAge(), Comparator.reverseOrder()) 
        .thenComparing(l->l.getCourse().getStudentSize());

无需逆序,就更简洁了:

return Comparator
        .comparing(l->l.getCourse().getTeacher().getAge()) 
        .thenComparing(l->l.getCourse().getStudentSize());

注意:在某些情况下,您需要显式声明泛型类型。例如,在 Java 8 中,如果没有 <FlightAssignment, LocalDateTime> 之前的 comparing(...),下面的代码将无法工作。

flightAssignmentList.sort(Comparator
        .<FlightAssignment, LocalDateTime>comparing(a -> a.getFlight().getDepartureUTCDateTime())
        .thenComparing(a -> a.getFlight().getArrivalUTCDateTime())
        .thenComparing(FlightAssignment::getId));

较新的 java 版本具有更好的自动类型检测功能,可能不需要。

【讨论】:

    【解决方案2】:

    不幸的是,java 中没有很好的语法。

    如果您想重用比较器的某些部分,我可以看到两种方法:

    • 通过组合比较器

      return comparing(Lecture::getCourse, comparing(Course::getTeacher, comparing(Teacher::getAge)))
             .thenComparing(Lecture::getCourse, comparing(Course::getStudentSize));
      
      // or with separate comparators
      Comparator<Teacher> byAge = comparing(Teacher::getAge);
      Comparator<Course> byTeacherAge = comparing(Course::getTeacher, byAge);
      Comparator<Course> byStudentsSize = comparing(Course::getStudentSize);
      return comparing(Lecture::getCourse, byTeacherAge).thenComparing(Lecture::getCourse, byStudentsSize);
      
    • 通过组合 getter 函数

      Function<Lecture, Course> getCourse = Lecture::getCourse;            
      return comparing(getCourse.andThen(Course::getTeacher).andThen(Teacher::getAge))
             .thenComparing(getCourse.andThen(Course::getStudentSize));
      
      // or with separate getters
      Function<Lecture, Course> getCourse = Lecture::getCourse;
      Function<Lecture, Integer> teacherAge = getCourse.andThen(Course::getTeacher).andThen(Teacher::getAge);
      Function<Lecture, Integer> studentSize = getCourse.andThen(Course::getStudentSize);
      return comparing(teacherAge).thenComparing(studentSize);
      

    【讨论】:

      【解决方案3】:
      import java.util.ArrayList;
      import java.util.Comparator;
      import java.util.List;
      import java.util.function.Function;
      
        class Person {
            String name ;
            PersonalDetail pDetail;
          public Person(String name, PersonalDetail pDetail) {
              super();
              this.name = name;
              this.pDetail = pDetail;
          }
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
          public PersonalDetail getpDetail() {
              return pDetail;
          }
          public void setpDetail(PersonalDetail pDetail) {
              this.pDetail = pDetail;
          }
      
      
      
      
        }
      
        class PersonalDetail{
            BirthDate birthDate;
      
          public BirthDate getBirthDate() {
              return birthDate;
          }
      
          public void setBirthDate(BirthDate birthDate) {
              this.birthDate = birthDate;
          }
      
          public PersonalDetail(BirthDate birthDate) {
              super();
              this.birthDate = birthDate;
          }
      
      
        }
      
          class BirthDate {
              public String getBirthdate() {
                  return birthdate;
              }
      
              public void setBirthdate(String birthdate) {
                  this.birthdate = birthdate;
              }
      
          String birthdate;
      
              public BirthDate(String birthdate) {
                  super();
      
                  this.birthdate = birthdate;
              }  
        }
      
        public class Test1 {
             public static void main(String[] args) {
                 BirthDate b1 = new BirthDate("2019-08-08");
                 BirthDate b2 = new BirthDate("2025-09-09");
                 BirthDate b3 = new BirthDate("2025-09-08");
                 BirthDate b4 = new BirthDate("2024-09-08");
      
                 PersonalDetail pd1  = new PersonalDetail(b1);
                 PersonalDetail pd2  = new PersonalDetail(b2);
                 PersonalDetail pd3  = new PersonalDetail(b3);
                 PersonalDetail pd4  = new PersonalDetail(b4);
      
                 Person p1  = new Person("P1",pd1);
                 Person p2  = new Person("P2",pd2);
                 Person p3  = new Person("P3",pd3);
                 Person p4  = new Person("P4",pd4);
      
                 List<Person> persons = new ArrayList();
                 persons.add(p1);
                 persons.add(p2);
                 persons.add(p3);
                 persons.add(p4);
      
                 Function<Person, PersonalDetail> getCourse = Person::getpDetail;  
      
                 Person minByAge = persons.stream()
                            .max(Comparator.comparing(getCourse.andThen(PersonalDetail::getBirthDate).andThen(BirthDate::getBirthdate))).get();
      
      
                System.out.println(maxByAge.getName());
      
             }
      
          }
      

      【讨论】:

      • 你能添加一些文字来解释你的代码是如何解决这个问题的吗?
      • 我正在根据子对象 (PersonalDetail-> BirthDate->birthdate) 字段过滤具有最大生日的人对象
      最近更新 更多