htz htz - 6 months ago 29
Java Question

How to shorten Comparator with multiple fields?

I am trying to write a Comparator in java8 which depends on two criteria with the use of lambda. I have a

List
persons. A
Person
has this methods:

Person{
String getFirstName();
String getLastName();
int getHeight();
Date getBirthday();
}


The sorting of the list is depending on two criterias which can be chosen by will. So it could be that the list should be sorted be firstname and birthday or by firstname and height and so on.

My approach was to create a switch-case block in which I look at the different combinations of criteria. But this approach is getting too big.

switch (holder.criteria1) {
case FIRSTNAME:
switch (holder.criteria2) {
case FIRSTNAME:
list.sort(Comparator.comparing(Person::getFirstName,
Comparator.nullsFirst(String::compareTo)));
break;
case LASTNAME:
list.sort(Comparator.comparing(Person::getFirstname,
Comparator.nullsFirst(String::compareTo)).thenComparing(
Person::getLastName, Comparator.nullsFirst(String::compareTo)));
break;
case HEIGHT:
list.sort(Comparator.comparing(Person::getFirstname,
Comparator.nullsFirst(String::compareTo)).thenComparing(
Person::getHeight, Comparator.nullsFirst(Integer::compareTo)));
break;
case BIRTHDAY:
list.sort(Comparator.comparing(Person::getFirstname,
Comparator.nullsFirst(String::compareTo)).thenComparing(
Person::getBirthday, Comparator.nullsFirst(Date::compareTo)));
break;
}
break;


I have to repeat this for every case combination. Three out of four cases of the criteria2 are nearly the same, only the methodnames and types change. This is a very ugly and long code and I would like to re-design it in a better way.

Is there a way to shorten this by using reflection perhaps?

Answer

You can store the comparators in your enum values, and then combine on demand:

enum SortOn {
    FIRSTNAME(Comparator.comparing(Person::getFirstName,
        Comparator.nullsFirst(String::compareTo))),
    LASTNAME(Comparator.comparing(Person::getLastName, 
        Comparator.nullsFirst(String::compareTo))),
    HEIGHT(Comparator.comparing(Person::getHeight, 
        Comparator.nullsFirst(Integer::compareTo))),
    BIRTHDAY(Comparator.comparing(Person::getBirthday, 
        Comparator.nullsFirst(Date::compareTo)));

    public final Comparator<Person> comparator;

    private SortOn(Comparator<Person> comparator) {
        this.comparator = comparator;
    }
}
...
public void sort(SortOn criteria1, SortOn criteria2) {
    if(criteria1 == criteria2) {
        list.sort(criteria1.comparator);
    } else {
        list.sort(criteria1.comparator.thenComparing(criteria2.comparator));
    }
}