Collections類提供了兩個sort方法,目標都是List<T> list,不同時可選擇自己指定一個Comparator。
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
調用的都是list的sort,如下:
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
調用的是Arrays的sort:
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
期三行調用的sort:
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
具體的排序細節就不展示了,最后兩個片段可以看出。Array提供了兩種排序算法,MergeSort和TimSort。默認用的是Java7新提供的TimSort,通過在啟動參數中指定-Djava.util.Arrays.useLegacyMergeSort=true可以用原來的MergeSort。這里的TimSort不少人被一個細節坑過,異常如下:
Exception in thread "main" java.lang.IllegalArgumentException: Comparison
method violates its general contract!
原因是,使用TimSort時,對象間的對比較更加嚴格,當指定compare時,必須保證以下三點:
a). sgn(compare(x, y)) == -sgn(compare(y, x))
b). (compare(x, y)>0) && (compare(y, z)>0) 意味着 compare(x, z)>0
c). compare(x, y)==0 意味着對於任意的z:sgn(compare(x, z))==sgn(compare(y, z)) 均成立
b). (compare(x, y)>0) && (compare(y, z)>0) 意味着 compare(x, z)>0
c). compare(x, y)==0 意味着對於任意的z:sgn(compare(x, z))==sgn(compare(y, z)) 均成立
后兩點很容易滿足,大多數問題都出在第一點上,例如我曾經的這個實現:
public int compare(Test o1, Test o2) {
return o1.getValue() > o2.getValue() ? 1 : -1;
}
乍一看沒什么毛病,但是對比上面的條件a,就會發現不滿足了,那就是:當o1的value與o2的value相等時!
解決方法:
public int compare(Test o1, Test o2) {
return o1.getValue() == o2.getValue() ? 0 : (o1.getValue() > o2.getValue() ? 1 : -1);
}
有空會補上MergeSort和TimSort的介紹。
