Java集合排序功能實現分析,java集合排序功能

來源:互聯網
上載者:User

Java集合排序功能實現分析,java集合排序功能

Java如何?集合的排序?

- 本文以對Student對象集合為例進行排序
Java通過Collections.sort(List<Student> stuList)和Collections.sort(List<Student> stuList,Comparator c)兩種方法實現排序。

用Collections.sort(List list) 方法實現排序:

step1: 確保Student類實現了Comparable介面,並重寫了compareTo()方法。

step2:調用Collections.sort(List list) 方法進行排序。

 1 public class Student implements Comparable<Student> { 2  3     private int age; 4  5     public Student(int age) { 6         this.age = age; 7     } 8  9     public int getAge() {10         return age;11     }12 13     @Override14     public int compareTo(Student student) {  // 重寫compareTo方法15 16         return (this.age < student.age) ? -1 : ((this.age == student.age) ? 0 : 1);17     }18 19 20     public static void main(String[] args) {21         List<Student> stuList = new ArrayList();22         stuList.add(new Student(5));23         stuList.add(new Student(3));24         stuList.add(new Student(7));25         stuList.add(new Student(2));26         stuList.add(new Student(4));27         stuList.add(new Student(6));28         stuList.add(new Student(1));29 30         Collections.sort(stuList);  // 調用排序方法31 32         for (Student student : stuList) {33             System.out.println(student.getAge());34         }35     }36 }

原理分析:

step1: Collections類調用List.sort(Comparator c)方法,比較子c賦值為null.

1     public static <T extends Comparable<? super T>> void sort(List<T> list) {2         list.sort(null);3     }

step2: List介面中的sort方法將stuList集合轉換成數組,通過Arrays.sort()方法對其進行排序,並將排序後的元素替換stuList中每個元素。

1    default void sort(Comparator<? super E> c) {2         Object[] a = this.toArray();3         Arrays.sort(a, (Comparator) c);4         ListIterator<E> i = this.listIterator();5         for (Object e : a) {6             i.next();7             i.set((E) e);8         }9     }

那到底時在哪裡調用的compareTo方法的呢?

進入Arrays.sort()方法:

 1     public static <T> void sort(T[] a, Comparator<? super T> c) { 2         if (c == null) { 3             sort(a); 4         } else { 5             if (LegacyMergeSort.userRequested) 6                 legacyMergeSort(a, c); 7             else 8                 TimSort.sort(a, 0, a.length, c, null, 0, 0); 9         }10     }

沒有制定比較子,因此c==null為true,執行sort(a)方法:

1 public static void sort(Object[] a) {2         if (LegacyMergeSort.userRequested)3             legacyMergeSort(a);4         else5             ComparableTimSort.sort(a, 0, a.length, null, 0, 0);6     }

LegacyMergeSort.userRequested預設為false,表示是否使用傳統歸併排序,傳統歸併排序在1.5及之前是預設排序方法,1.5之後預設執行ComparableTimSort.sort()方法。除非程式中強制要求使用傳統歸併排序。語句如下:

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

所以繼續看ComparableTimSort.sort()方法:

 1     static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) { 2         assert a != null && lo >= 0 && lo <= hi && hi <= a.length; 3  4         int nRemaining  = hi - lo; 5         if (nRemaining < 2) 6             return;  // Arrays of size 0 and 1 are always sorted 7  8         // If array is small, do a "mini-TimSort" with no merges 9         if (nRemaining < MIN_MERGE) {10             int initRunLen = countRunAndMakeAscending(a, lo, hi);11             binarySort(a, lo, hi, lo + initRunLen);12             return;13         }14 15         /**16          * March over the array once, left to right, finding natural runs,17          * extending short natural runs to minRun elements, and merging runs18          * to maintain stack invariant.19          */20         ComparableTimSort ts = new ComparableTimSort(a, work, workBase, workLen);21         int minRun = minRunLength(nRemaining);22         do {23             // Identify next run24             int runLen = countRunAndMakeAscending(a, lo, hi);25 26             // If run is short, extend to min(minRun, nRemaining)27             if (runLen < minRun) {28                 int force = nRemaining <= minRun ? nRemaining : minRun;29                 binarySort(a, lo, lo + force, lo + runLen);30                 runLen = force;31             }32 33             // Push run onto pending-run stack, and maybe merge34             ts.pushRun(lo, runLen);35             ts.mergeCollapse();36 37             // Advance to find next run38             lo += runLen;39             nRemaining -= runLen;40         } while (nRemaining != 0);41 42         // Merge all remaining runs to complete sort43         assert lo == hi;44         ts.mergeForceCollapse();45         assert ts.stackSize == 1;46     }

line4的nRemaining表示沒有排序的對象個數,方法執行前,如果這個數小於2,就不需要排序了。

如果2<= nRemaining <=32,即MIN_MERGE的初始值,表示需要排序的數組是小數組,可以使用mini-TimSort方法進行排序,否則需要使用歸併排序。

mini-TimSort排序方法:先找出數組中從下標為0開始的第一個升序序列,或者找出降序序列後轉換為升序重新放入數組,將這段升序數組作為初始數組,將之後的每一個元素通過二分法排序插入到初始數組中。注意,這裡就調用到了我們重寫的compareTo()方法了。

擷取初始數組的方法:

 1     private static int countRunAndMakeAscending(Object[] a, int lo, int hi) { 2         assert lo < hi; 3         int runHi = lo + 1; 4         if (runHi == hi) 5             return 1; 6  7         // Find end of run, and reverse range if descending 8         if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending 9             while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)10                 runHi++;11             reverseRange(a, lo, runHi);12         } else {                              // Ascending13             while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)14                 runHi++;15         }16 17         return runHi - lo;18     }

根據程式中舉例,a[1].compareTo(a[0]) <0,所以向下迴圈查看a[2].compareTo(a[1]) <0、a[3].compareTo(a[2]) <0等等是否成立,我們發現a[2].compareTo(a[1]) <0不成立,所以迴圈終止,擷取到最長的降序數組為a[]{5,3},再調用reverseRange()方法將其升序排列為a[]{3,5},作為初始數組,initRunLen=2。隨後進行二分法插入操作,代碼如下:

 1 private static void binarySort(Object[] a, int lo, int hi, int start) { 2         assert lo <= start && start <= hi; 3         if (start == lo) 4             start++; 5         for ( ; start < hi; start++) { 6             Comparable pivot = (Comparable) a[start]; 7  8             // Set left (and right) to the index where a[start] (pivot) belongs 9             int left = lo;10             int right = start;11             assert left <= right;12             /*13              * Invariants:14              *   pivot >= all in [lo, left).15              *   pivot <  all in [right, start).16              */17             while (left < right) {18                 int mid = (left + right) >>> 1;19                 if (pivot.compareTo(a[mid]) < 0)20                     right = mid;21                 else22                     left = mid + 1;23             }24             assert left == right;25 26             /*27              * The invariants still hold: pivot >= all in [lo, left) and28              * pivot < all in [left, start), so pivot belongs at left.  Note29              * that if there are elements equal to pivot, left points to the30              * first slot after them -- that's why this sort is stable.31              * Slide elements over to make room for pivot.32              */33             int n = start - left;  // The number of elements to move34             // Switch is just an optimization for arraycopy in default case35             switch (n) {36                 case 2:  a[left + 2] = a[left + 1];37                 case 1:  a[left + 1] = a[left];38                          break;39                 default: System.arraycopy(a, left, a, left + 1, n);40             }41             a[left] = pivot;42         }43     }

迴圈下標>=2的所有元素,通過二分法將其插入到初始數組中的適當位置,這樣,通過調用元素的compareTo()方法進行排序的功能實現完畢。

用Collections.sort(List list,Comparator c) 方法實現排序:

該方法傳入一個比較子,用於比較各元素的大小。該方法不需要元素實現Comparable介面,但需要一個實現Comparator介面的實作類別來執行個體化一個比較子,注意,這裡的Comparator是一個介面而非類。這裡通常採用匿名內部類的方法。

1 Collections.sort(stuList, new Comparator<Student>() {2             @Override3             public int compare(Student stu1, Student stu2) {4                 return (stu1.getAge() < stu2.getAge()) ? -1 : (stu1.getAge() == stu2.getAge() ? 0 : 1);5             }6         });

這種方法實現排序的方式與上述方法基本相同。

先調用Collections.sort()方法,傳入集合和比較子,sort()方法調用List的sort方法,傳入比較子。(同上step1)代碼如下:

1     public static <T> void sort(List<T> list, Comparator<? super T> c) {2         list.sort(c);3     }

 

List中sort()方法調用Arrays.sort()方法,傳入數組和比較子。(同上step2)

1 default void sort(Comparator<? super E> c) {2     Object[] a = this.toArray();3     Arrays.sort(a, (Comparator) c);4     ListIterator<E> i = this.listIterator();5     for (Object e : a) {6         i.next();7         i.set((E) e);8     }9 }

Arrays.sort方法調用TimSort.sort()方法,代碼如下:

 1 public static <T> void sort(T[] a, Comparator<? super T> c) { 2         if (c == null) { 3             sort(a); 4         } else { 5             if (LegacyMergeSort.userRequested) 6                 legacyMergeSort(a, c); 7             else 8                 TimSort.sort(a, 0, a.length, c, null, 0, 0); 9         }10     }

legacyMergeSort(a,c)和TimSort.sort()方法中與方法一不同的地方只有一點,即方法一中使用a.compareTo(b)進行比較而方法二中使用comparator.compare(a,b)進行比較,其他均相同。

 1 private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi, 2                                                     Comparator<? super T> c) { 3         assert lo < hi; 4         int runHi = lo + 1; 5         if (runHi == hi) 6             return 1; 7  8         // Find end of run, and reverse range if descending 9         if (c.compare(a[runHi++], a[lo]) < 0) { // Descending10             while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)11                 runHi++;12             reverseRange(a, lo, runHi);13         } else {                              // Ascending14             while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)15                 runHi++;16         }17 18         return runHi - lo;19     }

總結:

1.Collections.sort()排序有兩種實現方式,一是讓元素類實現Comparable介面並覆蓋compareTo()方法,二是給Collecitons.sort()方法傳入比較子,通常採用匿名內部內的方式傳入。

2.Collections.sort()通過調用Arrays.sort()方法進行排序,在Java1.6+中,如果集合大小<32則採用Tim-Sort演算法,如果>=32則採用歸併排序。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.