比较器.comparing()奇怪的行为/不按预期工作
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了比较器.comparing()奇怪的行为/不按预期工作相关的知识,希望对你有一定的参考价值。
根据我的知识,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( "
.............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部分不是。
有人可以解释一下吗?
如果你把每个电话放在一条线上,就会更容易理解发生了什么:
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)
那时,只有开始指数的比较才会逆转。
把reverse()的调用放在thenComparing中:
Comparator.comparingInt(Amount::getLineNum)
.thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
.thenComparingDouble( Amount::getValue );
来自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)
以上是关于比较器.comparing()奇怪的行为/不按预期工作的主要内容,如果未能解决你的问题,请参考以下文章
Visual Studio C++ 编译器在局部变量对象上的奇怪行为