Java的Arrays.sort()方法到底用的什么排序算法


  暫時網上看過很多JDK8中Arrays.sort的底層原理,有些說是插入排序,有些說是歸並排序,也有說大於域值用計數排序法,否則就使用插入排序。。。其實不全對。讓我們分析個究竟:

1 // Use Quicksort on small arrays
2 if (right - left < QUICKSORT_THRESHOLD) 
3 { 
4        //QUICKSORT_THRESHOLD = 286
5         sort(a, left, right, true);        
6         return;
7  }

  數組一進來,會碰到第一個閥值QUICKSORT_THRESHOLD(286),注解上說,小過這個閥值的進入Quicksort (快速排序),其實並不全是,點進去sort(a, left, right, true);方法:

1 // Use insertion sort on tiny arrays
2 if (length < INSERTION_SORT_THRESHOLD) 
3 {        
4     if (leftmost) 
5     {
6         ......

  點進去后我們看到第二個閥值INSERTION_SORT_THRESHOLD(47),如果元素少於47這個閥值,就用插入排序,往下看確實如此:

 1 /*
 2  * Traditional (without sentinel) insertion sort,
 3  * optimized for server VM, is used in case of
 4  * the leftmost part.
 5  */
 6 for (int i = left, j = i; i < right; j = ++i) 
 7 {               
 8      int ai = a[i + 1];               
 9      while (ai < a[j]) 
10      {
11           a[j + 1] = a[j];                   
12           if (j-- == left) 
13           {       
14                break;
15            }
16       }
17       a[j + 1] = ai;

 

元素少於47用插入排序

  至於大過INSERTION_SORT_THRESHOLD(47)的,用一種快速排序的方法:

  1.從數列中挑出五個元素,稱為 “基准”(pivot);

  2.重新排序數列,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分區退出之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作;

  3.遞歸地(recursive)把小於基准值元素的子數列和大於基准值元素的子數列排序。

 

 

快速排序(Quick Sort)  

  這是少於閥值QUICKSORT_THRESHOLD(286)的兩種情況,至於大於286的,它會進入歸並排序(Merge Sort),但在此之前,它有個小動作:

 1 // Check if the array is nearly sorted
 2     for (int k = left; k < right; run[count] = k) {        if (a[k] < a[k + 1]) { // ascending
 3             while (++k <= right && a[k - 1] <= a[k]);
 4         } else if (a[k] > a[k + 1]) { // descending
 5             while (++k <= right && a[k - 1] >= a[k]);            for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {                int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
 6             }
 7         } else { // equal
 8             for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {                if (--m == 0) {
 9                     sort(a, left, right, true);                    return;
10                 }
11             }
12         }        /*
13          * The array is not highly structured,
14          * use Quicksort instead of merge sort.
15          */
16         if (++count == MAX_RUN_COUNT) {
17             sort(a, left, right, true);            return;
18         }
19     }

  這里主要作用是看他數組具不具備結構:實際邏輯是分組排序,每降序為一個組,像1,9,8,7,6,8。9到6是降序,為一個組,然后把降序的一組排成升序:1,6,7,8,9,8。然后最后的8后面繼續往后面找。

  每遇到這樣一個降序組,++count,當count大於MAX_RUN_COUNT(67),被判斷為這個數組不具備結構(也就是這數據時而升時而降),然后送給之前的sort(里面的快速排序)的方法(The array is not highly structured,use Quicksort instead of merge sort.)

  如果count少於MAX_RUN_COUNT(67)的,說明這個數組還有點結構,就繼續往下走下面的歸並排序。

總結:

  從上面分析,Arrays.sort並不是單一的排序,而是插入排序,快速排序,歸並排序三種排序的組合,為此我畫了個流程圖:

 

   O(nlogn)只代表增長量級,同一個量級前面的常數也可以不一樣,不同數量下面的實際運算時間也可以不一樣。

  數量非常小的情況下(就像上面說到的,少於47的),插入排序等可能會比快速排序更快。 所以數組少於47的會進入插入排序。  

  快排數據越無序越快(加入隨機化后基本不會退化),平均常數最小,不需要額外空間,不穩定排序。

  歸排速度穩定,常數比快排略大,需要額外空間,穩定排序。

  所以大於或等於47或少於286會進入快排,而在大於或等於286后,會有個小動作:“// Check if the array is nearly sorted”。這里第一個作用是先梳理一下數據方便后續的歸並排序,第二個作用就是即便大於286,但在降序組太多的時候(被判斷為沒有結構的數據,The array is not highly structured,use Quicksort instead of merge sort.),要轉回快速排序。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM