Java的Arrays.sort()方法排序算法實例分析
暫時網上看過很多JDK8中Arrays.sort的底層原理,有些說是插入排序,有些說是歸并排序,也有說大于域值用計數排序法,否則就使用插入排序。。。其實不全對。讓我們分析個究竟:
// Use Quicksort on small arrays if (right - left < QUICKSORT_THRESHOLD) { //QUICKSORT_THRESHOLD = 286 sort(a, left, right, true); return; }
數組一進來,會碰到第一個閥值QUICKSORT_THRESHOLD(286),注解上說,小過這個閥值的進入Quicksort (快速排序),其實并不全是,點進去sort(a, left, right, true);方法:
// Use insertion sort on tiny arrays if (length < INSERTION_SORT_THRESHOLD) { if (leftmost) { ......
點進去后我們看到第二個閥值INSERTION_SORT_THRESHOLD(47),如果元素少于47這個閥值,就用插入排序,往下看確實如此:
/* * Traditional (without sentinel) insertion sort, * optimized for server VM, is used in case of * the leftmost part. */ for (int i = left, j = i; i < right; j = ++i) { int ai = a[i + 1]; while (ai < a[j]) { a[j + 1] = a[j]; if (j-- == left) { break; } } a[j + 1] = ai;
元素少于47用插入排序
至于大過INSERTION_SORT_THRESHOLD(47)的,用一種快速排序的方法:
1.從數列中挑出五個元素,稱為 “基準”(pivot);
2.重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的后面(相同的數可以到任一邊)。在這個分區(qū)退出之后,該基準就處于數列的中間位置。這個稱為分區(qū)(partition)操作;
3.遞歸地(recursive)把小于基準值元素的子數列和大于基準值元素的子數列排序。
快速排序(Quick Sort)
這是少于閥值QUICKSORT_THRESHOLD(286)的兩種情況,至于大于286的,它會進入歸并排序(Merge Sort),但在此之前,它有個小動作:
// Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending 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; } } else { // equal for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { if (--m == 0) { sort(a, left, right, true); return; } } } /* * The array is not highly structured, * use Quicksort instead of merge sort. */ if (++count == MAX_RUN_COUNT) { sort(a, left, right, true); return; } }
這里主要作用是看他數組具不具備結構:實際邏輯是分組排序,每降序為一個組,像1,9,8,7,6,8。9到6是降序,為一個組,然后把降序的一組排成升序:1,6,7,8,9,8。然后最后的8后面繼續(xù)往后面找。
每遇到這樣一個降序組,++count,當count大于MAX_RUN_COUNT(67),被判斷為這個數組不具備結構(也就是這數據時而升時而降),然后送給之前的sort(里面的快速排序)的方法(The array is not highly structured,use Quicksort instead of merge sort.)
如果count少于MAX_RUN_COUNT(67)的,說明這個數組還有點結構,就繼續(xù)往下走下面的歸并排序。
總結:
從上面分析,Arrays.sort并不是單一的排序,而是插入排序,快速排序,歸并排序三種排序的組合,為此我畫了個流程圖:
O(nlogn)只代表增長量級,同一個量級前面的常數也可以不一樣,不同數量下面的實際運算時間也可以不一樣。
數量非常小的情況下(就像上面說到的,少于47的),插入排序等可能會比快速排序更快。 所以數組少于47的會進入插入排序。
快排數據越無序越快(加入隨機化后基本不會退化),平均常數最小,不需要額外空間,不穩(wěn)定排序。
歸排速度穩(wěn)定,常數比快排略大,需要額外空間,穩(wěn)定排序。
所以大于或等于47或少于286會進入快排,而在大于或等于286后,會有個小動作:“// Check if the array is nearly sorted”。這里第一個作用是先梳理一下數據方便后續(xù)的歸并排序,第二個作用就是即便大于286,但在降序組太多的時候(被判斷為沒有結構的數據,The array is not highly structured,use Quicksort instead of merge sort.),要轉回快速排序。
到此這篇關于Java的Arrays.sort()方法排序算法實例分析的文章就介紹到這了,更多相關Java的Arrays.sort()方法內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java實現將字符串中首字母轉換成大寫,其它全部轉換成小寫的方法示例
這篇文章主要介紹了java實現將字符串中首字母轉換成大寫,其它全部轉換成小寫的方法,涉及java字符串遍歷、轉換、拼接等相關操作技巧,需要的朋友可以參考下2019-06-06Spring的循環(huán)依賴、三級緩存解決方案源碼詳細解析
這篇文章主要介紹了Spring的循環(huán)依賴、三級緩存解決方案源碼詳細解析,在Spring中,由于IOC的控制反轉,創(chuàng)建對象不再是簡單的new出來,而是交給Spring去創(chuàng)建,會經歷一系列Bean的生命周期才創(chuàng)建出相應的對象,需要的朋友可以參考下2024-01-01