在使用jdk1.7后发现,部分使用排序的列表变了。然后就可劲的找原因。最后发现:
首先进入Collection.sort方法
public static <T extends Comparable<? super T>> void sort(List<T> list) { Object[] a = list.toArray(); Arrays.sort(a); ListIterator<T> i = list.listIterator(); for (int j=0; j<a.length; j++) { i.next(); i.set((T)a[j]); } }
本质就是对数组进行排序然后输出。
再进入Arrays.sort()方法
public static void sort(Object[] a) { if (LegacyMergeSort.userRequested) legacyMergeSort(a); else ComparableTimSort.sort(a); }
看到这,其实也里看到点东西了,哈哈userRequested,再点进去
static final class LegacyMergeSort { private static final boolean userRequested = java.security.AccessController.doPrivileged( new sun.security.action.GetBooleanAction( "java.util.Arrays.useLegacyMergeSort")).booleanValue(); }
原来如此,在JVM中配置-Djava.util.Arrays.useLegacyMergeSort=true ,就可以使用传统的归并排序了。
到此为止,已经解决为什么JDK1.7的排序会与JDK1.6不致的问题的。下面我们来深入的看下这两个排序方式的迥异:
legacyMergeSort()核心实现
private static void mergeSort(Object[] src, Object[] dest, int low, int high, int off) { int length = high - low; // Insertion sort on smallest arrays if (length < INSERTIONSORT_THRESHOLD) { for (int i=low; i<high; i++) for (int j=i; j>low && ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--) swap(dest, j, j-1); return; } // Recursively sort halves of dest into src int destLow = low; int destHigh = high; low += off; high += off; int mid = (low + high) >>> 1; mergeSort(dest, src, low, mid, -off); mergeSort(dest, src, mid, high, -off); // If list is already sorted, just copy from src to dest. This is an // optimization that results in faster sorts for nearly ordered lists. if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) { System.arraycopy(src, low, dest, destLow, length); return; } // Merge sorted halves (now in src) into dest for(int i = destLow, p = low, q = mid; i < destHigh; i++) { if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0) dest[i] = src[p++]; else dest[i] = src[q++]; } }
排序个数小于7,直接使用插入排序,时间复杂度为O(n2),适用于小数据排序。其它部分典型的归并排序实现法。不过人家写的确实高效率 int mid = (low + high) >>> 1; 给我们肯定直接就成了int mid=(low+high)/2;
ComparableTimSort.sort(a)核心实现
rangeCheck(a.length, lo, hi); int nRemaining = hi - lo; if (nRemaining < 2) return; // Arrays of size 0 and 1 are always sorted // If array is small, do a "mini-TimSort" with no merges if (nRemaining < MIN_MERGE) { int initRunLen = countRunAndMakeAscending(a, lo, hi); binarySort(a, lo, hi, lo + initRunLen); return; } /** * March over the array once, left to right, finding natural runs, * extending short natural runs to minRun elements, and merging runs * to maintain stack invariant. */ ComparableTimSort ts = new ComparableTimSort(a); int minRun = minRunLength(nRemaining); do { // Identify next run int runLen = countRunAndMakeAscending(a, lo, hi); // If run is short, extend to min(minRun, nRemaining) if (runLen < minRun) { int force = nRemaining <= minRun ? nRemaining : minRun; binarySort(a, lo, lo + force, lo + runLen); runLen = force; } // Push run onto pending-run stack, and maybe merge ts.pushRun(lo, runLen); ts.mergeCollapse(); // Advance to find next run lo += runLen; nRemaining -= runLen; } while (nRemaining != 0); // Merge all remaining runs to complete sort assert lo == hi; ts.mergeForceCollapse(); assert ts.stackSize == 1;
ComparableTimSort来源于GOOGLE,详见下文
两个对象直接比如定义Comparator
public double compare(Object o1, Object o2) { ComparaContainer tr1 = (ComparaContainer) o1; ComparaContainer tr2 = (ComparaContainer) o2; if (tr1.getCompareValue() < tr2.getCompareValue()) { return (tr1.getCompareValue()-tr2.getCompareValue()); } else if (tr1.getCompareValue() == tr2.getCompareValue()) { return tr1.getSecondValue() <= tr2.getSecondValue() ? 0 : 1; } else { return 1; } }
由于1.7改得更为严谨不能直接使用-1,0,1来表示小于、等于、大于
得改用a-b
如下所示
public int compare(Object o1, Object o2) { ComparaContainer tr1 = (ComparaContainer) o1; ComparaContainer tr2 = (ComparaContainer) o2; double m = tr1.getCompareValue()-tr2.getCompareValue(); if (tr1.getCompareValue() < tr2.getCompareValue()) { return (int)m; } else if (tr1.getCompareValue() == tr2.getCompareValue()) { m = tr1.getSecondValue()- tr2.getSecondValue(); return (int)m; } else { return (int)m; } }
或者最简单的是将原来return 0;的改成return -1;