You can use Comparator.reverseOrder() to have a comparator giving the reverse of the natural ordering.
If you want to reverse the ordering of an existing comparator, you can use Comparator.reversed().
Sample code:
Stream.of(1, 4, 2, 5)
.sorted(Comparator.reverseOrder());
// stream is now [5, 4, 2, 1]
Stream.of("foo", "test", "a")
.sorted(Comparator.comparingInt(String::length).reversed());
// stream is now [test, foo, a], sorted by descending length
Answer from Tunaki on Stack OverflowYou can use Comparator.reverseOrder() to have a comparator giving the reverse of the natural ordering.
If you want to reverse the ordering of an existing comparator, you can use Comparator.reversed().
Sample code:
Stream.of(1, 4, 2, 5)
.sorted(Comparator.reverseOrder());
// stream is now [5, 4, 2, 1]
Stream.of("foo", "test", "a")
.sorted(Comparator.comparingInt(String::length).reversed());
// stream is now [test, foo, a], sorted by descending length
You can also use Comparator.comparing(Function, Comparator)
It is convenient to chain comparators when necessary, e.g.:
Comparator<SomeEntity> ENTITY_COMPARATOR =
Comparator.comparing(SomeEntity::getProperty1, Comparator.reverseOrder())
.thenComparingInt(SomeEntity::getProperty2)
.thenComparing(SomeEntity::getProperty3, Comparator.reverseOrder());
Videos
It's easier to understand what's going on if you put each call on a line:
Comparator.comparingInt(Amount::getLineNum)
.thenComparingInt(Amount::getStartIndex)
.reversed()
.thenComparingDouble(Amount::getValue)
That reversed() returns a comparator which reverses the results of the comparator it's called on... which is "the comparator which first compares the line number, then the start index." It's not like it's "bracketed" to just the scope of the previous thenComparingInt() call, which is how your previous formatting made it look like.
You could do it as:
Comparator.comparingInt(Amount::getLineNum)
.thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
.thenComparingDouble(Amount::getValue)
At that point it's only the start index comparison that's reversed.
From The docs:
reversed(): Returns a comparator that imposes the reverse ordering of this comparator.
thenComparing(): Returns a lexicographic-order comparator with another comparator. If this Comparator considers two elements equal, i.e. compare(a, b) == 0, other is used to determine the order.
Each step creates a new comparator based on the previous one. So the reversed() method creates a reversed comparator of
Comparator.comparingInt(Amount::getLineNum).thenComparingInt(Amount::getStartIndex)
To get only the second one reversed you should wrap it in an own comparator:
.thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
In your second solution the result is correct, because you are actually reversing the first condition twice:
Comparator.comparingInt(Amount::getLineNum).reversed() // reverses one time
.thenComparingInt(Amount::getStartIndex).reversed() // reverses all before (also the first one)
So the complete solution would look like this:
Comparator.comparingInt(Amount::getLineNum)
.thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
.thenComparingDouble(Amount::getValue)
This is a weakness in the compiler's type inferencing mechanism. In order to infer the type of u in the lambda, the target type for the lambda needs to be established. This is accomplished as follows. userList.sort() is expecting an argument of type Comparator<User>. In the first line, Comparator.comparing() needs to return Comparator<User>. This implies that Comparator.comparing() needs a Function that takes a User argument. Thus in the lambda on the first line, u must be of type User and everything works.
In the second and third lines, the target typing is disrupted by the presence of the call to reversed(). I'm not entirely sure why; both the receiver and the return type of reversed() are Comparator<T> so it seems like the target type should be propagated back to the receiver, but it isn't. (Like I said, it's a weakness.)
In the second line, the method reference provides additional type information that fills this gap. This information is absent from the third line, so the compiler infers u to be Object (the inference fallback of last resort), which fails.
Obviously if you can use a method reference, do that and it'll work. Sometimes you can't use a method reference, e.g., if you want to pass an additional parameter, so you have to use a lambda expression. In that case you'd provide an explicit parameter type in the lambda:
userList.sort(Comparator.comparing((User u) -> u.getName()).reversed());
It might be possible for the compiler to be enhanced to cover this case in a future release.
You can work around this limitation by using the two-argument Comparator.comparing with Comparator.reverseOrder() as the second argument:
users.sort(comparing(User::getName, reverseOrder()));