Reverse a comparator in Java 8
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
Java 8 Comparator chaining with a reverse on single property
Change the last bit to:
.thenComparing(Student::getID, Comparator.reverseOrder())
Or, if you want to avoid autoboxing:
.thenComparing(Comparator.comparingInt(Student::getID).reversed())
Comparator .comparing().reversed() strange behaviour / not working as expected
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.
Comparator.reversed() does not compile using lambda
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.
How to use a Java8 lambda to sort a stream in reverse order?
You can adapt the solution you linked in How to sort ArrayList<Long> in Java in decreasing order? by wrapping it in a lambda:
.sorted((f1, f2) -> Long.compare(f2.lastModified(), f1.lastModified())
note that f2 is the first argument of Long.compare
, not the second, so the result will be reversed.
Reversing sorting order of a stream using Comparator.reversed()
Map.Entry.comparingByKey
You can pass Comparator.reverseOrder()
to Map.Entry.comparingByKey
.
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.function.Function;
import java.util.stream.Collectors;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
List<String> words = Arrays.asList(input.split(" "));
words.stream()
.map(word -> word.replaceAll("[^A-Za-z0-9]", ""))
.map(String::toLowerCase)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
.limit(10)
.sorted(Comparator.comparing(Map.Entry::getValue))
.forEach(e -> System.out.println(e.getKey()));
}
}
A sample run:
1 2 3 4 5 6 7 8 9 1 2 5 9 2 7 2 3 9 5 3 1 8 3 6 8 2 3 6 9
4
7
8
6
5
1
9
3
2
Comparator.comparing().reversed() reverses all the earlier comparators?
Since you define a comparator like Comparator.comparingInt(Triple::getA)
this returns a new
.thenComparingInt(Triple::getB)Comparator
object that contains both previous ways of comparing the object. If you then call reversed
on this Comparator
it returns new Comparator which reverses this Comparator
- so in this case both previous conditions will be reversed.
If you want to reverse one of your comparators in the chain you can use something like :
.thenComparing(Triple::getB, Comparator.reverseOrder())
So one of your comparators can look like :
Comparator.comparingInt(Triple::getA)
.thenComparing(Triple::getB, Comparator.reverseOrder())
.thenComparing(Triple::getC)
This will only reverse B property sorting condition.
Is it possible to sort objects by two fields both in reversed natural order using a Comparator.comparingInt chain
just remove reversed()
from comparingInt
and only call reversed
upon thenComparingLong
:
Comparator<Test> comparator =
Comparator.comparingInt((Test t) -> t.height) // <--- removed reverse from this comparator
.thenComparingLong((Test t ) -> t.width).reversed();
Further, given that width
is an int
I'd use thenComparingInt
instead of thenComparingLong
.
In addition, in regard to your stream pipeline, I'd suggest using forEachOrdered
since you care about the order in which the elements are printed.
forEach
is documented as:
The behavior of this operation is explicitly nondeterministic. For
parallel stream pipelines, this operation does not guarantee to
respect the encounter order of the stream, as doing so would sacrifice
the benefit of parallelism.
Therefore:
list.stream()
.sorted(comparator)
.forEachOrdered(e -> System.out.println(e.height + "/" + e.width));
Java Comparator using .reverseOrder() but with an inner class
ArtistCompare artistCompare = new ArtistCompare();
Collections.sort(songList, Collections.reverseOrder(artistCompare));
Edit July 2015
As this answer still gets some attention, here a small update:
With Java SE 8 it's becoming easier to create a reversed comparator:
Comparator<Song> songRatingComparator = Comparator.comparing(Song::getRating);
Collections.sort(songList, songRatingComparator.reversed());
And you can, of course, also use the Streams framework:
List<Song> sortedSongList = songList.stream()
.sorted(Comparator.comparing(Song::getRating).reversed())
.collect(Collectors.toList());
Related Topics
How to Determine If a Date Is Between Two Dates in Java
Make Arraylist.Toarray() Return More Specific Types
Java Linkedhashmap Get First or Last Entry
Stand-Alone Java Code Formatter/Beautifier/Pretty Printer
Gradle: Could Not Determine Java Version from '11.0.2'
Lambda Expression to Convert Array/List of String to Array/List of Integers
How to Disable Jsessionid in Tomcat Servlet
How to Format Localdate Object to Mm/Dd/Yyyy and Have Format Persist
Easy Way of Running the Same Junit Test Over and Over
Try-Catch-Finally-Return Clarification
Why Java.Util.Optional Is Not Serializable, How to Serialize the Object with Such Fields
How to Properly Express Jpql "Join Fetch" with "Where" Clause as JPA 2 Criteriaquery
Jtable with Titled Rows and Columns
How to Enumerate All Classes in a Package and Add Them to a List