昨天遇到一个很奇怪的异常,异常信息如下:
Exception in thread "main" 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:1462)
at com.example.testaspect.demo.TestC2.sortDateAndTimestamps(TestC2.java:33)
at com.example.testaspect.demo.TestC2.main(TestC2.java:50)
报错的代码如下:
list.sort((o1, o2) -> {
if (o1 != null && o2 != null) {
return o1.compareTo(o2);
}
return 0;
});
看着语法好像也没什么问题啊,感觉很奇怪。而且在本地复现也没有复现,查看源码也是云里雾里的,说原因是判断一个len2==0了报错了,但是中间的处理逻辑十分复杂难懂,就在网上找答案。
很多解答说可以配置Java环境
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
也就是使用JDK1.6之前排序算法,但是总也觉得这不是一个很妥善的处理方式。
网上一篇解决这个问题的博文
Sort algorithm changes for Java 7 throws IllegalArgumentException
说这个问题是Java1.7使用了新的排序TimSort的问题,但是这个复现的是使用了Date和Timestamp,好像也和我遇到的不太一样,我用String的,也遇到了这样的问题。
后来,将null加入到了List中,居然真的复现了。
总结一下这个复现的特点:
- String类型的List需要存入null才会出现这个异常
- 数据条数太少的时候很难复现
- 异常并不是每次都会复现,需要多次运行才可能复现
复现的代码如下:
List<String> list = new ArrayList<>();
for (int i = 0; i < 31; i++) {
list.add("i=" + i);
}
list.add(null);
for (int i = 0; i < 200000; i++) {
System.out.println(i);
Collections.shuffle(list);
list.sort((o1, o2) -> {
if (o1 != null && o2 != null) {
return o1.compareTo(o2);
}
return 0;
});
System.out.println(list);
}
经过多次运行,发现31条数据以上才更容易复现,也不是很明白为什么。那么怎么解决呢?
经过验证发现,以下方式修改,就不会再有这个异常了。
list.sort((o1, o2) -> {
if (o1 != null && o2 != null) {
return o1.compareTo(o2);
}
if (o1 != null) {
return 1;
}
if (o2 != null) {
return -1;
}
return 0;
});
也就是增加了对“o1为空,o2不为空”、“o2为空,o1不为空”这两种情况的处理。也就是为了满足以下特性,根据具体场景进行修改
1) 自反性:x,y 的比较结果和 y,x 的比较结果相反。
2) 传递性:x>y,y>z,则 x>z。
3) 对称性:x=y,则 x,z 比较结果和 y,z 比较结果相同。
毕竟如果某一个为空,其他不为空默认认为它们是相等的也确实不合理。
总结,在JDK1.7之后,排序算法进行了修改,对排序规则的要求更高了,所以会报这个 异常。
解决方案
- 使用JDK1.6之前的方式:
- 在代码中的运行类添加环境配置代码:
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
- 在运行参数添加以下参数:
-Djava.util.Arrays.useLegacyMergeSort=true
- 在编写代码时,根据具体比较,对不同的情况下本身不相等的情况下,不要不要把它们定义为相同,一定要进行区分,如String类就需要用
list.sort((o1, o2) -> { //都不为null if (o1 != null && o2 != null) { return o1.compareTo(o2); } //o1不为null,o2为null if (o1 != null) { return 1; } //o1为null,o2不为null if (o2 != null) { return -1; } //都为null return 0; } );
版权声明:程序员胖胖胖虎阿 发表于 2022年11月13日 上午6:32。
转载请注明:Exception in thread “main“ java.lang.IllegalArgumentException:解决方案 | 胖虎的工具箱-编程导航
转载请注明:Exception in thread “main“ java.lang.IllegalArgumentException:解决方案 | 胖虎的工具箱-编程导航
相关文章
暂无评论...