Exception in thread “main“ java.lang.IllegalArgumentException:解决方案

2年前 (2022) 程序员胖胖胖虎阿
205 0 0

昨天遇到一个很奇怪的异常,异常信息如下:

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中,居然真的复现了。

总结一下这个复现的特点:

  1. String类型的List需要存入null才会出现这个异常
  2. 数据条数太少的时候很难复现
  3. 异常并不是每次都会复现,需要多次运行才可能复现

复现的代码如下:

 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之后,排序算法进行了修改,对排序规则的要求更高了,所以会报这个 异常。

解决方案

  1. 使用JDK1.6之前的方式:
    1. 在代码中的运行类添加环境配置代码:
     System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); 
    
    1. 在运行参数添加以下参数:
    -Djava.util.Arrays.useLegacyMergeSort=true
    
  2. 在编写代码时,根据具体比较,对不同的情况下本身不相等的情况下,不要不要把它们定义为相同,一定要进行区分,如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;
    	}
    );
    

相关文章

暂无评论

暂无评论...