Можно ли сортировать объекты по двум полям в обратном естественном порядке, используя цепочку Comparator.comparingInt - PullRequest
0 голосов
/ 05 января 2019

Допустим, я хочу отсортировать объекты в ArrayList в поле height в обратном порядке, и если два значения совпадают, я хочу дополнительно отсортировать по ширине поля также в обратном порядке. Есть ли способ, используя что-то вроде

 Comparator<Test> comparator = Comparator
            .comparingInt((Test t) -> t.height).reversed()
            .thenComparingInt((Test t ) -> t.width).reversed();

Я знаю, что могу использовать что-то вроде:

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);
        }});

Но мне действительно любопытно, есть ли одноканальное решение

Так что относительно этого мини-примера

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;
}

Я получаю вывод:

1/1
2/3
2/1

потому что вторая reversed() переворачивает весь список. Есть ли способ получить вывод:

2/3
2/1
1/1

Ответы [ 2 ]

0 голосов
/ 05 января 2019

Чтобы завершить очень хороший ответ Аомин, я расскажу о возможностях и поведении, стоящих за ними.
Обратите внимание, что вы должны отдавать предпочтение получателям (а также ссылкам на методы) по сравнению с прямым доступом к полю. Поэтому я проиллюстрирую это. Я также буду полагаться на static import для Comparator статических методов, таких как import static java.util.Comparator.*;, чтобы сосредоточиться на важных вещах.

То, что вы делаете, отменяет начальное обратное Comparator на getHeight():

Comparator<Test> comparator = 
       comparingInt(Test::getHeight)
       .reversed() // 1)
       .thenComparingInt(Test::getWidth) // 2)
       .reversed(); // 3)

С точки зрения логики это означает:

1) Сортировка путем сравнения на оборотной стороне Test::getHeight.
2) Затем сортируйте, сравнивая по Test::getWidth.
3) полностью изменить логику сравнения.

Таким образом, вы получаете компаратор, который сортирует по Test::getHeight и затем сортирует на обороте Test::getWidth.

В решении, предоставленном Aomine:

Comparator<Test> comparator = 
       comparingInt(Test::getHeight) // 1)
       .thenComparingInt(Test::getWidth) // 2)
       .reversed(); // 3)

С точки зрения логики это означает:

1) Сортировка по сравнению с Test::getHeight.
2) Затем сортируйте, сравнивая по Test::getWidth.
3) полностью изменить логику сравнения.

Таким образом, вы получаете Comparator, который сортирует на оборотной стороне Test::getHeight и затем сортирует на обороте Test::getWidth.

Вы также можете написать код таким образом (хотя и более многословно, но интересно с точки зрения обучения):

Comparator<Test> comparator = 
       comparingInt(Test::getHeight) 
       .reversed() // 1)
       .thenComparing(comparingInt(Test::getWidth)
                      .reversed()); // 2)

С точки зрения логики это означает:

1) Сортировка путем сравнения на оборотной стороне Test::getHeight.
2) Затем сортируйте, сравнивая на обратной стороне Test::getWidth.

Это производит все еще Comparator, который сортирует на оборотной стороне Test::getHeight и затем сортирует на обороте Test::getWidth.

0 голосов
/ 05 января 2019

просто удалите reversed() из comparingInt и звоните только reversed при thenComparingLong:

Comparator<Test> comparator = 
      Comparator.comparingInt((Test t) -> t.height) // <--- removed reverse from this comparator
                .thenComparingLong((Test t ) -> t.width).reversed();

Далее, учитывая, что width - это int, я бы использовал thenComparingInt вместо thenComparingLong.

Кроме того, что касается вашего потокового конвейера, я бы предложил использовать forEachOrdered, поскольку вас волнует порядок , в котором печатаются элементы.

forEach задокументировано как:

Поведение этой операции явно недетерминировано. За параллельные потоки, эта операция не гарантирует уважать порядок встречи потока, так как это принесет в жертву благо параллелизма.

Таким образом:

 list.stream()
     .sorted(comparator)
     .forEachOrdered(e -> System.out.println(e.height + "/" + e.width));
...