java-collections.sort異常Comparison method violates its general contract!
阿新 • • 發佈:2018-12-29
異常資訊
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)的實現:
而1.5下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); }
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");