How to sort a list / stream using an unknown number of comparators?

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 an unknown number of comparators in a list and I want to sort them all? Is there anyway? Or is it better to use the old way of comparing models with multiple ifs?

+5
source share
3 answers

If you have several comparators in the list or in any other collection, you can replace them with one by performing a shortcut on Stream :

 List<Comparator<String>> comparators = ... Comparator<String> combined = comparators.stream() .reduce(Comparator::thenComparing) .orElse(someDefaultComparator); // empty list case 

All instances will be grouped together using thenComparing according to their order from the input list.


The same could be implemented using a non-stream approach using a simple for loop:

 Comparator<String> result = comparators.get(0); for (int i = 1; i < comparators.size(); i++) { result = result.thenComparing(comparators.get(i)); } 
+14
source

You can reduce stream of comparators to one comparator by calling .thenComparing() on the accumulated comparator and the current comparator on 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 the result of shrinking the wrapper with Optional.ofNullable() to handle the case when with an empty list of comparators. Then you can decide when you pass the result to the sorted() method, what to do in the case of an empty list - you can use the comparator (a,b)->0 , which does not sort anything.

Then no matter how many comparators you want to apply. But there is one , but - the order of the comparators in the questions of collection. In this example, I use comparators in ascending order (starting from the first element of the list to the last). This affects the sorting result to a large extent.

For example, in your example, you call byB.thenComparing(byA) . It produces a 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

+6
source

If you are allowed to use google guava, you can simply connect your Comparables.

https://google.imtqy.com/guava/releases/snapshot/api/docs/com/google/common/collect/Ordering.html#compound-java.lang.Iterable-

Comparator comparator = Ordering.compound (comparators);

If no comparators are specified, the original order will be preserved.

Javadoc guava by this method, although it offers for Java 8:

 list.stream().sorted(comparators.stream().reduce(defaultComparator, Comparator::thenComparing)); 

Therefore, a connect solution is recommended for Java 7 (or less).

0
source

Source: https://habr.com/ru/post/1270539/


All Articles