Lets say I want to sort objects in an ArrayList
on a field height
in reversed order and if two values are the same I want to further sort on the field width also in reversed order.
Is there a way by using something like
Comparator<Test> comparator = Comparator
.comparingInt((Test t) -> t.height).reversed()
.thenComparingInt((Test t ) -> t.width).reversed();
I know I could use something like:
Collections.sort(list, new Comparator<Test>() {
public int compare(Test o1, Test o2) {
Integer x1 = o1.height;
Integer x2 = o2.height;
int sComp = x2.compareTo(x1);
if (sComp != 0) {
return sComp;
}
x1 = o1.width;
x2 = o2.width;
return x2.compareTo(x1);
}});
But I'm really curious if there is one line solution
So regarding this mini-example
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
Test one = new Test();
one.height = 2;
one.width = 1;
Test two = new Test();
two.height = 2;
two.width = 3;
Test three = new Test();
three.height = 1;
three.width = 1;
Comparator<Test> comparator = Comparator
.comparingInt((Test t) -> t.height).reversed()
.thenComparingInt((Test t ) -> t.width).reversed();
List<Test> list = new ArrayList<>();
list.add(one);
list.add(two);
list.add(three);
list.stream()
.sorted(comparator)
.forEach(e -> System.out.println(e.height + "/" + e.width));
}
}
class Test {
int width;
int height;
}
I get the output:
1/1
2/3
2/1
because the second reversed()
reverses the whole list.
Is there a way to gean a output of:
2/3
2/1
1/1
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));
To complete the very good answer of Aomine I will expose the possibilities and the behavior behind them.
Note that you should favor getters (and also method references) over direct field access. So I will illustrate with that.
I will also rely on static import
for Comparator
static methods such as import static java.util.Comparator.*;
to focus on important things.
What you do actually cancels the initial reversed Comparator
on getHeight()
:
Comparator<Test> comparator =
comparingInt(Test::getHeight)
.reversed() // 1)
.thenComparingInt(Test::getWidth) // 2)
.reversed(); // 3)
In terms of logic it means :
1) Sort by comparing on the reverse of Test::getHeight
.
2) Then sort by comparing on Test::getWidth
.
3) Reverse the whole comparing logic.
So you get a Comparator that sorts on Test::getHeight
and
then sorts on the reverse of Test::getWidth
.
In the solution provided by Aomine :
Comparator<Test> comparator =
comparingInt(Test::getHeight) // 1)
.thenComparingInt(Test::getWidth) // 2)
.reversed(); // 3)
In terms of logic it means :
1) Sort by comparing on Test::getHeight
.
2) Then sort by comparing on Test::getWidth
.
3) Reverse the whole comparing logic.
So you get a Comparator that sorts on the reverse of Test::getHeight
and
then sorts on the reverse of Test::getWidth
.
You could also write the code in this way (while more verbose but interesting in terms of learning) :
Comparator<Test> comparator =
comparingInt(Test::getHeight)
.reversed() // 1)
.thenComparing(comparingInt(Test::getWidth)
.reversed()); // 2)
In terms of logic it means :
1) Sort by comparing on the reverse of Test::getHeight
.
2) Then sort by comparing on the reverse of Test::getWidth
.
That produces still a Comparator that sorts on the reverse of Test::getHeight
and
then sorts on the reverse of Test::getWidth
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With