异常信息
Format: Comparison method violates its general contract!
Params: null
StackTrace: java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:899)
at java.util.TimSort.mergeAt(TimSort.java:516)
at java.util.TimSort.mergeCollapse(TimSort.java:441)
at java.util.TimSort.sort(TimSort.java:245)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.ArrayList.sort(ArrayList.java:1454)
at
如何复现
下面的代码看似没什么问题,可以多执行几次,或者将循环32次调高一点。50次 100次。然后就会报同样的错误。
import java.util.*;
public class TestList {
public static void main(String[] args) {
//随机产生32个[0~3]的数字集合
Random random = new Random();
ArrayList<Integer> list = new ArrayList<>();
//这里为什么是32?
for (int i = 0; i < 32; i++) {
list.add(random.nextInt(4));
}
System.out.println(list);
list.sort((o1, o2) -> {
//定义一个不具备自反性的Comparator
if(o1>o2){
return 1;
}
return -1;
});
System.out.println(list);
}
}
解决方案
sort
里面通过Comparator
的comparing、comparingInt、comparingLong、comparingDouble
等方法代替,不要手动去返回1、-1、0
。 如
list.sort(Comparator.comparingInt(User::getSort))
或者判断的时候这样写,也不会报错。
list.sort((o1, o2) -> {
if(o1>o2){
return 1;
}
if(o1<o2){
return -1;
}
return 0;
});
结论
好了,看到这里你的问题已经解决了,可以静下心来看一看异常原因了。因为排序算法要求比较器Comparator满足:
-
自反性
-
传递性
-
对称性
三个条件的约束,好像没太懂?
简单看来,作为一个比较器来说,以上3个要求似乎合情合理,理所当然,但事实上,开发者很容易忽略一些特定的情况,因为以上3个要求对于排序来讲,并不是全部必要的。
换句话说,我们必须要处理得当,同时返回-1、0、1这三个变量。如果少了,在数据量小的情况下也许不会报错,在数据足够多,足够极端的情况下就会报错。 Collections.sort()在排序算法上的更新固然能够带来排序性能上的提升,但这一次排序算法的升级对比较器Comparator增加了一些规则,并没有完全向前兼容,更由于增加的规则是隐性的,这就使得开发人员在无意之间制造了线上环境“万万想不到”的异常,甚至造成线上环境的崩溃,产生损失。在一定的程度上甚至可以说,这一升级是得不偿失的。