是否可以使用 Comparator.comparingInt 链按两个字段以相反的自然顺序对对象进行排序

Is it possible to sort objects by two fields both in reversed natural order using a Comparator.comparingInt chain

假设我想以相反的顺序对字段 height 中的 ArrayList 中的对象进行排序,如果两个值相同,我想进一步对字段宽度进行排序也以相反的顺序. 有没有办法使用

之类的东西
 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

只需从 comparingInt 中删除 reversed() 并仅在 thenComparingLong 上调用 reversed:

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 记录为:

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.

因此:

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

为了完成 Aomine 的非常好的答案,我将揭示其背后的可能性和行为。
请注意,您应该优先使用 getter(以及方法引用)而不是直接字段访问。所以我会用那个来说明。 我还将依靠 static import 获得 Comparator 静态方法,例如 import static java.util.Comparator.*; 以专注于重要的事情。

您所做的实际上取消了 getHeight() 上的初始反转 Comparator

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 的相反顺序排序。

在青峰提供的解决方案中:

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

在逻辑上它的意思是:

1) 通过比较 Test::getHeight.
排序 2)然后通过比较 Test::getWidth.
进行排序 3) 反转整个比较逻辑。

所以你得到一个比较器,它按 Test::getHeight 的相反顺序排序,并且 然后按 Test::getWidth 的相反顺序排序。

你也可以这样写代码(虽然比较冗长,但学习起来很有趣):

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

在逻辑上它的意思是:

1) 按 Test::getHeight.
的相反顺序比较排序 2) 然后在Test::getWidth的反面比较排序。

这仍然会产生一个比较器,它按 Test::getHeight 的相反顺序排序,并且 然后按 Test::getWidth 的相反顺序排序。