I have the following snippet:
List<O> os = new ArrayList<>();
os.add(new O("A", 3, "x"));
os.add(new O("A", 2, "y"));
os.add(new O("B", 1, "z"));
Comparator<O> byA = Comparator.comparing(O::getA);
Comparator<O> byB = Comparator.comparing(O::getB);
// I want to use rather this...
List<Comparator<O>> comparators = new ArrayList<>();
comparators.add(byA);
comparators.add(byB);
os.stream()
.sorted(byB.thenComparing(byA))
.forEach(o -> System.out.println(o.getC()));
As you can see, I sort using explicitly two comparators. But what if I have the unknown number of comparators in some list and I want to sort by them all? Is there any way? Or should rather use the old fashion way comparator with multiple ifs?
Sorting a List of Integers with Stream. Found within the Stream interface, the sorted() method has two overloaded variations that we'll be looking into. This methods returns a stream consisting of the elements of the stream, sorted according to natural order - the ordering provided by the JVM.
We are sorting using the nullsFirst() method, after sorting, null values will come in the start and then the sorting list of employees will come sorted by date of birth. To sort on natural order, after ordering null values, use Comparator. nullsFirst( Comparator. naturalOrder() ).
If you have multiple comparators in a list or any other collection, you can replace them with a single one by performing the reduction on a Stream
:
List<Comparator<String>> comparators = ...
Comparator<String> combined = comparators.stream()
.reduce(Comparator::thenComparing)
.orElse(someDefaultComparator); // empty list case
All instances will be composed together using thenComparing
according to their order from the input list.
The same would be achievable using a non-stream approach by utilizing a simple for-loop:
Comparator<String> result = comparators.get(0);
for (int i = 1; i < comparators.size(); i++) {
result = result.thenComparing(comparators.get(i));
}
You can reduce
stream of comparators to a single comparator by calling .thenComparing()
on accumulated comparator and current comparator in the iteration:
Optional<Comparator<O>> comparator = Optional.ofNullable(comparators.stream()
.reduce(null, (acc, current) -> acc == null ? current : acc.thenComparing(current), (a, b) -> a));
os.stream()
.sorted(comparator.orElse((a,b) -> 0))
.forEach(o -> System.out.println(o.getC()));
In this example I use Optional<Comparator<O>>
and wrap reduction result with Optional.ofNullable()
to handle a case when with empty comparators list. Then you can decide when you pass a result to sorted()
method what to do in case of an empty list - you can use (a,b)->0
comparator that does not sort anything.
Then it doesn't matter how many comparators you want to apply. But there is one but - the order of comparators in given collection matters. In given example I apply comparators in ascending order (starting from the first element of the list to the last one). It affects the result of sorting heavily.
For example, in your example you call byB.thenComparing(byA)
. It produces different result then byA.thenComparing(byB)
. I can assume that in some cases you want to control the order in which comparators are applied.
Live demo:
https://jdoodle.com/a/4Xz
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