1. 程式人生 > >java-collections.sort異常Comparison method violates its general contract!

java-collections.sort異常Comparison method violates its general contract!

異常資訊

java.lang.IllegalArgumentException: Comparison method violates its general contract!
 at java.util.TimSort.mergeHi(TimSort.java:868)
  at java.util.TimSort.mergeAt(TimSort.java:485)
  at java.util.TimSort.mergeCollapse(TimSort.java:408)
at java.util.TimSort.sort(TimSort.java:214)
  at java.util.TimSort.sort(TimSort.java:
173) at java.util.Arrays.sort(Arrays.java:659) at java.util.Collections.sort(Collections.java:217) ...

原因

JDK7中的Collections.Sort方法實現中,如果兩個值是相等的,那麼compare方法需要返回0,否則 可能 會在排序時拋錯,而JDK6是沒有這個限制的。

if (len2 == 0) {
    throw new IllegalArgumentException("Comparison method violates its general contract!");
}
  關於1.7下Arrays.sort(Object[] a)的實現:
    public static void sort(Object[] a) {  
            if (LegacyMergeSort.userRequested)  
                legacyMergeSort(a);  
            else  
                ComparableTimSort.sort(a);  
        }  
        /** To be removed in a future release. */  
        private static void legacyMergeSort(Object[] a) {  
            Object[] aux = a.clone();  
            mergeSort(aux, a, 0, a.length, 0);  
        }  
 而1.5下Arrays.sort(Object[] a)的實現:
    public static void sort(Object[] a) {  
            Object[] aux = (Object[])a.clone();  
            mergeSort(aux, a, 0, a.length, 0);  
        }  
DK6到JDK7確實存在相容問題(不相容列表)。在不相容列表中我們可以找到關於Collections.sort的不相容說明,如下:
    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  

描述的意思是說,java.util.Arrays.sort(java.util.Collections.sort呼叫的也是此方法)方法中的排序演算法在JDK7中已經被替換了。如果違法了比較的約束新的排序演算法也許會丟擲llegalArgumentException異常。JDK6中的實現則忽略了這種情況。那麼比較的約束是什麼呢?看這裡,大體如下:


  • 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
    Collections.sort(list, new Comparator<Integer>() {  
        @Override  
        public int compare(Integer o1, Integer o2) {  
            return o1 > o2 ? 1 : -1;// 錯誤的方式  
        }  
    });  

當x == y時,sgn(compare(x, y))  = -1,-sgn(compare(y, x)) = 1,這違背了sgn(compare(x, y)) == -sgn(compare(y, x))約束,所以在JDK7中丟擲了本文標題的異常。

正確方式:

    Collections.sort(list, new Comparator<Integer>() {  
        @Override  
        public int compare(Integer o1, Integer o2) {  
            // return o1 > o2 ? 1 : -1;  
            return o1.compareTo(o2);// 正確的方式  
        }  
    });  

解決方案:

執行Collections.sort進行排序前可以呼叫:

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