Reverse a Comparator in Java 8

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)
.thenComparingInt(Triple::getB)
this returns a new 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



Leave a reply



Submit