比较器 .comparing().reversed() 奇怪的行为/没有按预期工作

Comparator .comparing().reversed() strange behaviour / not working as expected

据我所知,Comparator.comparingInt() 应按升序排序,Comparator.comparingInt().reversed 应按降序排序。但我发现这是相反的情况。

最好用一个例子来解释。以下是我的代码。

金额class:

class Amount
{
    int lineNum;
    int startIndex;
    Double value;
//Getters , setters and toString.
}

主要方法:

public static void main( String[] args )
{
    List<Amount> amounts = new ArrayList<>();
    amounts.add( new Amount( 1.0, 5, 10 ) ); //LINE_NUM 5
    amounts.add( new Amount( 3.0, 9, 30 ) );
    amounts.add( new Amount( 2.0, 3, 40 ) );
    amounts.add( new Amount( 9.0, 5, 20 ) ); //LINE_NUM 5
    amounts.add( new Amount( 6.0, 1, 50 ) );
    amounts.add( new Amount( 4.0, 5, 20 ) ); //LINE_NUM 5
    System.out.println( ".............BEFORE SORTING.........." );
    amounts.forEach( System.out::println );


    amounts.sort( 
                 Comparator.comparingInt( Amount::getLineNum )   //NOTE THIS
        .           .thenComparingInt( Amount::getStartIndex ).reversed()
                      .thenComparingDouble( Amount::getValue ) );

    System.out.println( "\n\n.............AFTER SORTING.........." );

    amounts.forEach( System.out::println );
}

我想要按 lineNum 升序、startIndex 降序和值升序排序的金额列表。

所以我的期望是这样。

.............AFTER SORTING..........(EXPECTATION)

Amount [lineNum=1, startIndex=50, value=6.0]

Amount [lineNum=3, startIndex=40, value=2.0]

Amount [lineNum=5, startIndex=20, value=4.0]

Amount [lineNum=5, startIndex=20, value=9.0]

Amount [lineNum=5, startIndex=10, value=1.0]

Amount [lineNum=9, startIndex=30, value=3.0]

.............AFTER SORTING..........(ACTUAL)

Amount [lineNum=9, startIndex=30, value=3.0]

Amount [lineNum=5, startIndex=20, value=4.0]

Amount [lineNum=5, startIndex=20, value=9.0]

Amount [lineNum=5, startIndex=10, value=1.0]

Amount [lineNum=3, startIndex=40, value=2.0]

Amount [lineNum=1, startIndex=50, value=6.0]

lineNum order 外,一切正常。金额按行号 降序 排序,而我希望它按 升序 .

当我将 Comparator 更改为 following

时,结果符合预期
amounts.sort(
    Comparator.
    comparingInt( Amount::getLineNum ).reversed()
    .thenComparingInt( Amount::getStartIndex ).reversed()
    .thenComparingDouble( Amount::getValue ) );

这很奇怪,因为 comparingInt( Amount::getLineNum ).reversed() 应该按行号降序对金额进行排序。

我注意到的另一件事是,比较 StartIndex 按预期工作。但是 比较 lineNumber 部分不是。

有人可以解释一下吗?

如果将每个调用都放在一条线上,则更容易理解发生了什么:

Comparator.comparingInt(Amount::getLineNum)
    .thenComparingInt(Amount::getStartIndex)
    .reversed()
    .thenComparingDouble(Amount::getValue)

那个 reversed() returns 一个比较器,它反转调用它的比较器的结果...这是 "the comparator which first compares the line number, then the start index." 它不像 "bracketed" 只是之前 thenComparingInt() 调用的范围,这是您之前的格式设置的样子。

您可以这样做:

Comparator.comparingInt(Amount::getLineNum)
    .thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
    .thenComparingDouble(Amount::getValue)

那个点,只有起始索引比较被颠倒了。

来自 docs:

reversed(): Returns a comparator that imposes the reverse ordering of this comparator.

thenComparing(): Returns a lexicographic-order comparator with another comparator. If this Comparator considers two elements equal, i.e. compare(a, b) == 0, other is used to determine the order.

每一步都会在前一步的基础上创建一个新的比较器。所以 reversed() 方法创建了一个 reversed comparator of

Comparator.comparingInt(Amount::getLineNum).thenComparingInt(Amount::getStartIndex)

要仅反转第二个,您应该将其包装在自己的比较器中:

.thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())

在你的第二个解决方案中,结果是正确的,因为你实际上将第一个条件颠倒了两次:

Comparator.comparingInt(Amount::getLineNum).reversed() // reverses one time
    .thenComparingInt(Amount::getStartIndex).reversed() // reverses all before (also the first one)

所以完整的解决方案如下所示:

Comparator.comparingInt(Amount::getLineNum)
    .thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
    .thenComparingDouble(Amount::getValue)

将 reversed() 的调用放在 thenComparing 中:

   Comparator.comparingInt(Amount::getLineNum)

   .thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
   .thenComparingDouble( Amount::getValue );