Collections.sort排序方法报IllegalArgumentException

鉴于大家搜索是为了解决问题,这里我先提供解决方案

1.给jvm添加启动参数。

-Djava.util.Arrays.useLegacyMergeSort=true

2.在排序方法之前添加如下代码:

System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); 

 3.修改比较方法,自己写的比较器只写了1和-1的情况,而没有写0。

PS:数组的大小必须大于等于32.小于32时不会出现java.lang.IllegalArgumentException异常.原因是:java中默认的MIN_MERGE为32.若待排序的数组小于MIN_MERGE时,会使用Binary Sort,而不会使用TimSort.

这个排序导致的异常将会在java7以上的版本出现,所以如果你的JDK从6升级到了7或者8,那一定要小心此异常。

在java7的兼容列表中,就有对此排序不兼容的说明:

Area: API: Utilities
Synopsis: Updated sort behavior for Arrays and Collections may throw an IllegalArgumentException
Description: The sorting algorithm used by java.util.Arrays.sort and (indirectly) by java.util.Collections.sort has been replaced. The new sort implementation may throw an IllegalArgumentException if it detects a Comparable that violates the Comparable contract. The previous implementation silently ignored such a situation.
If the previous behavior is desired, you can use the new system property, java.util.Arrays.useLegacyMergeSort, to restore previous mergesort behavior.
Nature of Incompatibility: behavioral
RFE: 6804124

java7开始引入了Timsort的排序算法,java7之前引入的是QuickSort。

排序算法比较的图:

可以发现,Timsort在表现上比QuickSort还要好。

Timsort结合了归并排序和插入排序。这个算法在实现过程中明确需要:严格的单调递增或者递减来保证算法的稳定性。

  • sgn(compare(x, y)) == -sgn(compare(y, x))
  • ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0
  • compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z

看上去很像离散数学课中学习的集合的对称性,传递性的关系。

所以异常的原因是因为排序算法不够严谨导致的,实际上业务上的代码经常不如纯技术上的严谨。

在我们自己写代码的过程中,如果比较的对象比较复杂,容易出错的部分是第一处  

对于函数sgn(compare(x,y)),由于compare(x,y)的返回结果有0、1、-1三种,sgn(x)的结果也有三种,

1、当compare(x,y) < 0 时,sgn(compare(x,y))结果为-1

2、当compare(x,y) = 0 时,sgn(compare(x,y))结果为0

3、当compare(x,y) > 0 时,sgn(compare(x,y))结果为1

最容易出错的情况就是自己写的比较器只写了1和-1的情况,而没有写0,如:

     return x > y ? 1 : -1;

这样会导至当x == y时,compare(x,y)的结果为 -1,此时sgn(compare(x,y)) = -1,这与第一种满足条件sgn(compare(x, y)) == -sgn(compare(y, x))相违背。所以会抛出IllegalArgumentException异常。

异常复现

List<Integer> ases = new ArrayList<>(Arrays.asList( -1, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0,
                    -1, 0, 1, 1, 1, 0, 0, 0, 0, -1, 1, 1, 1, 1, -1, -1, -1, -1,1));
                Collections.sort(ases, new Comparator<Integer>() {
                    @Override
                    public int compare(Integer a, Integer b) {
                        return a > b ? 1 : -1;
                    }
                });

PS:数组的大小必须大于等于32.小于32时不会出现java.lang.IllegalArgumentException异常.原因是:java中默认的MIN_MERGE为32.若待排序的数组小于MIN_MERGE时,会使用Binary Sort,而不会使用TimSort.

解决方法

public int compare(Integer a, Integer b) {
            return a == b ? 0 : (a > b ? 1 : -1);
        }

参考博客:https://blog.csdn.net/hspingcc/article/details/51896705###

https://blog.csdn.net/u010811263/article/details/81093825

猜你喜欢

转载自blog.csdn.net/huang957664022/article/details/97756232
今日推荐