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

Rot*_*cky 5 java reverse method-chaining comparator java-8

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

 Comparator<Test> comparator = Comparator
            .comparingInt((Test t) -> t.height).reversed()
            .thenComparingInt((Test t ) -> t.width).reversed();
Run Code Online (Sandbox Code Playgroud)

我知道我可以使用类似的东西:

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);
        }});
Run Code Online (Sandbox Code Playgroud)

但我真的很好奇是否有一行解决方案

所以关于这个小例子

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;
}
Run Code Online (Sandbox Code Playgroud)

我得到输出:

1/1
2/3
2/1
Run Code Online (Sandbox Code Playgroud)

因为第二个reversed()反转了整个列表。有没有办法让输出:

2/3
2/1
1/1
Run Code Online (Sandbox Code Playgroud)

Ous*_* D. 6

只是删除reversed()comparingInt和来电reversedthenComparingLong

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

此外,鉴于这width是一个int我会使用thenComparingInt而不是thenComparingLong.

此外,关于您的流管道,我建议使用,forEachOrdered因为您关心打印元素的顺序

forEach 记录为:

此操作的行为明显是不确定的。对于并行流管道,此操作不保证遵守流的遇到顺序,因为这样做会牺牲并行性的好处。

所以:

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


dav*_*xxx 5

为了完成 Aomine 的非常好的答案,我将揭露其背后的可能性和行为。
请注意,与直接字段访问相比,您应该更喜欢 getter(以及方法引用)。所以我会以此来说明。我还将依赖static import静态Comparator方法,例如 import static java.util.Comparator.*;专注于重要的事情。

你所做的实际上取消了最初的Comparator逆转getHeight()

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

从逻辑上来说,这意味着:

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)
Run Code Online (Sandbox Code Playgroud)

从逻辑上来说,这意味着:

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)
Run Code Online (Sandbox Code Playgroud)

从逻辑上来说,这意味着:

1) 通过与 的相反方向比较来排序Test::getHeight
2)然后通过与相反的比较排序Test::getWidth

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