比较器.comparing()行为异常/未按预期运行

时间:2019-04-03 16:53:09

标签: java sorting lambda comparator comparable

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

用一个例子可以更好地解释。以下是我的代码。

金额类别:

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降序和值升序排序。

所以我的期望是这样。

  

..............排序后..........(期望

     

金额[lineNum = 1,startIndex = 50,值= 6.0]

     

金额[lineNum = 3,startIndex = 40,value = 2.0]

     

金额[lineNum = 5,startIndex = 20,value = 4.0]

     

金额[lineNum = 5,startIndex = 20,value = 9.0]

     

金额[lineNum = 5,startIndex = 10,value = 1.0]

     

金额[lineNum = 9,startIndex = 30,value = 3.0]

     

.............排序后.........(实际

     

金额[lineNum = 9,startIndex = 30,value = 3.0]

     

金额[lineNum = 5,startIndex = 20,value = 4.0]

     

金额[lineNum = 5,startIndex = 20,value = 9.0]

     

金额[lineNum = 5,startIndex = 10,value = 1.0]

     

金额[lineNum = 3,startIndex = 40,value = 2.0]

     

金额[lineNum = 1,startIndex = 50,值= 6.0]

除了 lineNum order 之外,其他所有内容都正确。金额按 按lineNumber降序 排序,而我希望它按 升序

当我将Comparator更改为以下内容时,结果符合预期

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

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

我注意到的另一件事是,与StartIndex进行比较可以正常工作。但是按行号比较部分不是。

有人可以解释吗?

3 个答案:

答案 0 :(得分:10)

如果将每个呼叫放在一行,则更容易理解发生了什么:

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

那个reversed()返回一个比较器,该比较器反转它所调用的比较器的结果...这是“比较器首先比较行号,然后比较起始索引”。并不是像前面的thenComparingInt()调用那样将其“括起来”,这就是您以前的格式使它看起来像的样子。

您可以按照以下方式进行操作:

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

点上,只是反向的开始索引比较。

答案 1 :(得分:1)

来自docs

  

reversed():返回一个强加此比较器相反顺序的比较器。

     

thenComparing():返回带有另一个比较器的字典顺序比较器。如果该比较器认为两个元素相等,即compare(a,b)== 0,则使用其他元素确定顺序。

每一步都基于前一个创建一个新的比较器。因此,reversed()方法创建了

反向比较器
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)

答案 2 :(得分:1)

将调用的reversed()放在thenComparing中

   Comparator.comparingInt(Amount::getLineNum)

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