Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparator.comparing(...) of a nested field

Suppose I have a domain model like this:

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

Now I can create a Teacher Comparator like this:

    return Comparator             .comparing(Teacher::getAge); 

But how do I compare Lecture's on nested fields, like this?

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

I can't add a method Lecture.getTeacherAge() on the model.

like image 851
Geoffrey De Smet Avatar asked Jul 22 '16 18:07

Geoffrey De Smet


People also ask

What does a comparator compare?

A comparator interface is used to order the objects of user-defined classes. A comparator object is capable of comparing two objects of the same class.

How does comparator Compare method work?

The compare MethodIt returns a positive value if obj1 is greater than obj2. Otherwise, a negative value is returned. By overriding compare( ), you can alter the way that objects are ordered. For example, to sort in a reverse order, you can create a comparator that reverses the outcome of a comparison.

What is comparator comparing in Java?

Java Comparator interface is used to order the objects of a user-defined class. This interface is found in java. util package and contains 2 methods compare(Object obj1,Object obj2) and equals(Object element).

How do you compare two fields in Java?

Field equals() method in Java with Examples This method compares two field objects and returns true if both objects are equal otherwise false. The two Field objects are considered equal if and only if when they were declared by the same class and have the same name and type.


2 Answers

You can't nest method references. You can use lambda expressions instead:

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

Without the need for reverse order it's even less verbose:

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

Note: in some cases you need to explicitly state the generic types. For example, the code below won't work without the <FlightAssignment, LocalDateTime> before comparing(...) in Java 8.

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

Newer java version have better auto type detection and might not require that.

like image 57
Eran Avatar answered Oct 11 '22 05:10

Eran


Unfortunately there is no nice syntax in java for that.

If you want to reuse parts of comparator I can see 2 ways:

  • by composing comparators

    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); 
  • by composing getter functions

    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); 
like image 43
Nazarii Bardiuk Avatar answered Oct 11 '22 04:10

Nazarii Bardiuk