当比较器返回0时,java 8中的Collections.sort不能用作java 6
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了当比较器返回0时,java 8中的Collections.sort不能用作java 6相关的知识,希望对你有一定的参考价值。
最近我将我们的应用程序jdk从Java 6更新为Java 8,但仍将源语言级别保持为Java 6.更改后,我们的一个单元测试失败了。我注意到LinkedList的Collections.sort在Java 8和Java 6中的工作方式不同。即使我是JDk 1.8的Source Level java 8,我也会得到相同的不同行为。要重新创建问题:定义下面的枚举:
public enum Weight {
A(1), B(0), C(0), D(0), E(2);
public int getWeight() {
return weight;
}
private int weight;
Weight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return name() + '(' + weight + ')';
}
}
和一个主类如下:
public class Main {
public static void main(String[] args) {
List<Weight> weightList = new LinkedList<Weight>();
weightList.add(Weight.A);
weightList.add(Weight.B);
weightList.add(Weight.C);
weightList.add(Weight.D);
weightList.add(Weight.E);
Collections.sort(weightList, new Comparator<Weight>() {
@Override
public int compare(Weight o1, Weight o2) {
return o1.getWeight() > o2.getWeight()? 1:0;
}
});
System.out.print(weightList);
}
}
在Java 6下运行代码的输出是:“C: Program Files Java jdk1.6.0_45 bin java”
[B(0),C(0),D(0),A(1),E(2)]
和在java 8下运行代码的输出是:
“C: Program Files(x86) Java jdk1.8.0_161 bin java”
[A(1),B(0),C(0),D(0),E(2)]
我将类型从LinkedList
更改为ArrayList
,我得到了相同的结果但是如果我正在更改比较器,那么Java 8将对数组进行排序:
Collections.sort(weightList, new Comparator<Weight>() {
@Override
public int compare(Weight o1, Weight o2) {
return o1.getWeight() > o2.getWeight()? 1:-1;
}
});
如您所见,java 8似乎没有正确排序代码。 Java中是否有错误或者我像往常一样遗漏了什么?
来自JDK 7的内部排序算法was changed to Tim Sort for objects and a dual-pivot quicksort for primitives。
由于你的比较器是错误的(它为非等值返回0),你很幸运它没有破坏。现在它按预期打破了。
public int compare(Weight o1, Weight o2) {
return o1.getWeight() > o2.getWeight()? 1:0;
}
该定义不符合Java API预期的合同。如果你传递一个无效的sort
,Comparator
的行为是不确定的。
- 实施者必须确保所有
sgn(compare(x, y)) == -sgn(compare(y, x))
和x
的y
。- 实现者还必须确保关系是可传递的:
((compare(x, y)>0) && (compare(y, z)>0))
暗示compare(x, z)>0
。- 最后,执行者必须确保
compare(x, y)==0
暗示所有sgn(compare(x, z))==sgn(compare(y, z))
的z
。
如果-1
,你的函数不会返回o1 < o2
。它返回0
,错误地断言参数是相等的。这也会导致第一个子弹失败:如果你翻转参数,结果需要改变符号。
正确的实施将是:
public int compare(Weight o1, Weight o2) {
return Integer.compare(o1.getWeight(), o2.getWeight());
}
正如鲍里斯在评论中所说:你的compare()
方法不正确。
看看你的实现:
return o1.getWeight() > o2.getWeight()? 1:0;
假设您的列表中有三个对象:
- o1:重量3
- o2:重量5
- o3:重量5
现在假设sort()
以这种方式为你的列表调用compare()
:
compare(o1, o2)
返回0
compare(o2, o3)
返回0
通过传递性,这意味着o1
,o2
和o3
具有相同的顺序。你不想要我假设。
如果它适用于Java 6,它只是实现中的“机会”而不是你应该可靠的结果。
要解决您的问题,您必须处理3个案例(优等,劣等或相等):
@Override
public int compare(Weight o1, Weight o2) {
if (o1.getWeight() > o2.getWeight()){
return 1;
}
else if (o1.getWeight() < o2.getWeight()){
return -1;
}
return 0;
}
这最终等同于写:return Integer.compare(o1.getWeight(), o2.getWeight())
。
请注意,Java 8中更好的替代方法和less error prone正在使用Comparator.comparingInt()
工厂方法并直接使用sort()
中引入的List
方法:
weightList.sort(Comparator.comparingInt(Weight::getWeight));
以上是关于当比较器返回0时,java 8中的Collections.sort不能用作java 6的主要内容,如果未能解决你的问题,请参考以下文章