Comparing objects based on the values of multiple fields using Comparator

Володимир Гончаров :

How can I improve the comparator for objects by several fields? The comparator must sort user by last name or first name if last name doesn't exist. And if there are no last name and first name sort by username. But these users must be at the end of the list.

public static Comparator<UserConfigurationDto> BY_LASTNAME =  (u1, u2) -> {
        // if users have only username compare them
        if((u1.getLastName().isEmpty() && u1.getFirstName().isEmpty())
                && (u2.getLastName().isEmpty() && u2.getFirstName().isEmpty())){
            return u1.getUsername().compareToIgnoreCase(u2.getUsername());
        }
        //if user doesnt have firstName and LastName drop them at the end
        if(u1.getLastName().isEmpty() && u1.getFirstName().isEmpty()){
            return 1000000 + getWeight(u1.getUsername());
        }
        if(u2.getLastName().isEmpty() && u2.getFirstName().isEmpty()){
            return -1000000 + getWeight(u2.getUsername());
        }
      String s1 = u1.getLastName().isEmpty() ? u1.getFirstName() : u1.getLastName();
      String s2 = u2.getLastName().isEmpty() ? u2.getFirstName() : u2.getLastName();
      return s1.compareToIgnoreCase(s2);
    };
}
private static int getWeight(String s){
    return s.codePoints().sum();
}

Does anybody have an idea how to improve this? I try to use Comparator.comparing and Comparator.thenComparing but they produce an incorrect result

davidxxx :

1) return 1000000 + getWeight(u1.getUsername()); and return -1000000 + getWeight(u2.getUsername()); are not required. return 1 and return -1 is clearer and produces the same result if you refer to the CompareTo() javadoc :

Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object

2) You don't chain the field comparisons but you have 3 ways of sorting according to the state of the compared objects. So the fact that the code be a bit verbose to define each case is finally normal.
You could all the same reduce it with an extract method as you duplicate a lot of user.getLastName().isEmpty() invocations.

For example :

public static Comparator<UserConfigurationDto> BY_LASTNAME =  (u1, u2) -> {

        // first case
        if( u1.isLastAndFirstNameEmpty() && u2.isLastAndFirstNameEmpty()){
            return u1.getUsername().compareToIgnoreCase(u2.getUsername());
        }
        // second case
        if(u1.isLastAndFirstNameEmpty()){
            return 1;
        }
        else if(u2.isLastAndFirstNameEmpty()){
            return -1;
        }
        // third case
        String s1 = u1.getLastName().isEmpty() ? u1.getFirstName() : u1.getLastName();
        String s2 = u2.getLastName().isEmpty() ? u2.getFirstName() : u2.getLastName();
        return s1.compareToIgnoreCase(s2);
    };

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325554&siteId=1