【问题标题】:Comparator .comparing().reversed() strange behaviour / not working as expectedComparator .comparing().reversed() 奇怪的行为/没有按预期工作
【发布时间】:2019-08-25 07:00:48
【问题描述】:

据我所知,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 降序和 value 升序排序的数量列表。

所以我的期望是这样的。

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

金额 [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 order之外,一切都是正确的。金额按 lineNumber 降序排序,而我预计它会按升序

当我将比较器更改为跟随时,结果符合预期

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

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

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

谁能解释一下?

【问题讨论】:

    标签: java sorting lambda comparator comparable


    【解决方案1】:

    如果你把每个调用放在一行上,就会更容易理解发生了什么:

    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)
    

    那个点,只有开始索引比较被反转。

    【讨论】:

    • 你的意思是,任何比较器上的 reversed() 也适用于所有以前的比较器?
    • @ArunGowda 它被应用于thenComparingInt 方法的结果,即Comparator 合并之前的方法
    • @ArunGowda:你在比较器上调用它。它反转了该比较器的结果。 Comparator.comparingInt(Amount::getLineNum).thenComparingInt(Amount::getStartIndex) 生成的比较器是“按行号排序然后开始索引的比较器”。因此,当您在该比较器上调用 reverse 时,它会返回“(按行号排序然后开始索引的比较器) - 但顺序相反”
    • 或许更容易理解:comp1 = Comparator.comparingInt(Amount::getLineNum);comp2 = comp1.thenComparingInt(Amount::getStartIndex);comp3 = comp2.reversed();
    • 我正在尝试使用它,但我是在第一个和第二个比较器颠倒的情况下,只有第三个不是。在这种情况下,Comparator.comparing(Comparator.comparingInt(Amount::getLineNum).reversed()).thenComparing... 似乎不再起作用了。我必须在括号外使用reversedComparator.comparingInt(Amount::getLineNum).reversed().thenComparing...。为什么thenComparing 可以,comparing 不行?我知道这是不同的方法签名,我只是想知道我是否以正确的方式做。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      来自docs

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

      thenComparing():返回一个字典顺序比较器和另一个比较器。如果此 Comparator 认为两个元素相等,即 compare(a, b) == 0,则使用 other 来确定顺序。

      每个步骤都会基于前一个步骤创建一个新的比较器。所以reversed() 方法创建了一个 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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-20
        • 2015-03-14
        • 2021-05-31
        • 2021-10-19
        • 2020-03-18
        • 2012-06-14
        • 2014-11-15
        相关资源
        最近更新 更多