最近线上报了个异常,java.lang.IllegalArgumentException: Comparison method violates its general contract!;代码定位如下:
通过改为如下方式,可以解决问题:
或者
下面给出一个代码示例,可以模拟运行报错:
public static void main(String[] args) {
while(true) {
Random random = new Random();
List<Integer> list = new ArrayList<>();
for(int i=0;i<32;i++) {
list.add(random.nextInt(16));
}
System.out.println(list);
list.sort((c1,c2)->{
return c1>c2?-1:1;
});
System.out.println(list);
}
}
运行很快会报错:
通过报错信息,我们看到报错的是一个TimSort类,起始位置时Arrays.sort方法:
关于jdk1.8中Arrays.sort方法,其大致算法思想如下图:
其中TimSort算法是重点,TimSort算法是结合了插入排序和合并排序思想,基于现实数据通常是部分有序的状态,而优化出的更有效率的排序算法,它由Tim Peters于2002年实施并首次使用在Python编程语言中,java在jdk1.7版本开始引入TimSort算法,并兼容保留了jdk1.6版本的排序算法, 可以通过System.setProperty("java.util.Arrays.useLegacyMergeSort", "true") 来指定使用旧版本算法(报错的代码在此算法下是不会有问题的)。
TimSort是稳定的算法,对于比较器要满足如下约束:
1、f(x,y) == -f(y,x);
2、if f(x,y)>0 and f(y,z)>0 then f(x,z)>0;
3、if f(x,y)==0 and f(x,z)>0 then f(y,z)>0;
再看我们开始的代码,如果c1==c2,f(c1,c2)=1 and f(c2,c1)=1,就违反了第一个约束,所以我们一开始的代码在运行过程中才会报错。