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

问题描述 投票:5回答:3

根据我的知识,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 );
}

我想要按行号递增排序的金额列表,按startIndex降序和值升序排序。

所以我的期望是这样的。

...............分类后..........(期望)

金额[lineNum = 1,startIndex = 50,value = 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,value = 6.0]

除了lineNum订单,一切都是正确的。金额按行数降序排序,而我期望它按升序排列。

当我将比较器更改为以下时,结果如预期

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

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

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

有人可以解释一下吗?

java sorting lambda comparator comparable
3个回答
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
投票

把reverse()的调用放在thenComparing中:

   Comparator.comparingInt(Amount::getLineNum)

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

1
投票

来自docs

reversed():返回一个比较器,强制执行此比较器的反向排序。

thenComparing():返回一个带有另一个比较器的字典顺序比较器。如果该比较器认为两个元素相等,即比较(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)
© www.soinside.com 2019 - 2024. All rights reserved.