background

In everyday development, you give a List and return it in descending order with two attributes. The comparator defaults to sort in ascending order,

The reversed() method of the Comparator is often used to generate a descending Comparator. Since both properties are in descending order, it would be natural to call the reversed() method twice, but the sorted result is not correct.

🤔 reversed() is the only method used in a reversed() project. I decided to take a look at the reversed() source code.

Company business code is inconvenient to disclose, write a simple code to elaborate on the event

StudentInfo list. You want to sort it in descending order by score, and then by age if score is equal

public class StudentInfo { private String name; private int age; private int score; } public static void main(String[] args) { StudentInfo a = new StudentInfo("cici", 17, 80); StudentInfo b = new StudentInfo("tony", 18, 80); StudentInfo c = new StudentInfo("mark", 19, 79); List<StudentInfo> list = new ArrayList<>(); list.add(a); list.add(b); list.add(c); // Take it for granted list.sort(Comparator.comparing(StudentInfo::getScore).reversed().thenComparing(StudentInfo::getAge).reversed()); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i).toString()); }Copy the code

   

The running results are as follows:

The second property age is sorted in descending order, but the first property score is not sorted in descending order. Why?

Source code analysis

List.sort()

First look at the source of list.sort

Arrays.sort(), so the list.sort () method is a custom Comparator.

To implement a comparator instance, we need to override one of the abstract methods in compare().

Comparator.comparing()

Trace comparator.paring () to build the comparator source

Function is a Function interface that contains an apply() method to implement method calls.

Parameter Function
keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor extends U> keyExtractor

Function<StudentInfo, Integer> getScore = StudentInfo::getScore;
Copy the code

CompareTo () method, two Integer types score are compared

The source code implements an anonymous class Comparator that overwrites compare(), the only abstract method in the Comparator, by comparing compareTo() methods with two Integer types of score.

reversed()

Borrowed reverseOrder() from the collection class

 

Looking at the code, we generate a ReverseComparator2, a static inner class for Collections that implements the Comparator interface.

The principle of descending order is as follows: The original ReverseComparator2 held the original comparator CMP internally, and the compare() method was rewritten. When CMP was called internally, the order of T1 and T2 was switched to achieve the effect of reverse order.

I suddenly understand the code in the diagram above

if (cmp instanceof ReverseComparator2)
Copy the code

If two reversed() methods are written in tandem, the original comparator will be returned directly.

thenComparing()

Generate another Comparator

An anonymous class that overrides the compare method and combines the two comparators that perform compare() to compare elements. It’s kind of recursive.

Puzzles to solve

Now at the last critical moment, the last tail executes the reversed() method, which is equivalent to reversing the whole sort in the new comparator, but the first one has already been reversed, and reversing it again will restore the ascending effect. Let’s do the code in reversed()

conclusion

The reversed() method is very error-prone without looking at the source code carefully, and feels like an accumulation effect.

In order not to confuse errors, it is recommended when building constructors

Comparator.comparing(StudentInfo::getScore, Collections.reverseOrder())
Copy the code

This creates a constructor directly, so it doesn’t matter if there are other constructors in front of it.

It is also meaningful to continue to delve deeply into problems encountered at ordinary times.