剖析JDK8中Arrays.sort底層原理及其排序算法的選擇

寫這篇文章的初衷,是想寫篇Java和算法的實(shí)際應(yīng)用专甩,讓算法不再玄乎钟鸵,而Arrays.sort是很好的切入點(diǎn),即分析Java的底層原理涤躲,又能學(xué)習(xí)里面的排序算法思想棺耍。希望能給在座各位在工作中或面試中一點(diǎn)幫助!轉(zhuǎn)載請(qǐng)注明出處:Michael孟良

點(diǎn)進(jìn)sort方法:

      // Use Quicksort on small arrays
      if (right - left < QUICKSORT_THRESHOLD) {//QUICKSORT_THRESHOLD = 286
        sort(a, left, right, true);
        return;
      }

數(shù)組一進(jìn)來种樱,會(huì)碰到第一個(gè)閥值QUICKSORT_THRESHOLD(286)蒙袍,注解上說俊卤,小過這個(gè)閥值的進(jìn)入Quicksort (快速排序),其實(shí)并不全是害幅,點(diǎn)進(jìn)去sort(a, left, right, true);方法:

// Use insertion sort on tiny arrays
    if (length < INSERTION_SORT_THRESHOLD) {
        if (leftmost) {
        ......

點(diǎn)進(jìn)去后我們看到第二個(gè)閥值INSERTION_SORT_THRESHOLD(47)消恍,如果元素少于47這個(gè)閥值,就用插入排序以现,往下看確實(shí)如此:

            /*
             * 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.從數(shù)列中挑出五個(gè)元素,稱為 “基準(zhǔn)”(pivot)邑遏;
2.重新排序數(shù)列佣赖,所有元素比基準(zhǔn)值小的擺放在基準(zhǔn)前面,所有元素比基準(zhǔn)值大的擺在基準(zhǔn)的后面(相同的數(shù)可以到任一邊)记盒。在這個(gè)分區(qū)退出之后茵汰,該基準(zhǔn)就處于數(shù)列的中間位置。這個(gè)稱為分區(qū)(partition)操作孽鸡;
3.遞歸地(recursive)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序。


快速排序(Quick Sort)

這是少于閥值QUICKSORT_THRESHOLD(286)的兩種情況栏豺,至于大于286的彬碱,它會(huì)進(jìn)入歸并排序(Merge Sort),但在此之前奥洼,它有個(gè)小動(dòng)作:

    // 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;
        }
    }

這里主要作用是看他數(shù)組具不具備結(jié)構(gòu):實(shí)際邏輯是分組排序巷疼,每降序?yàn)橐粋€(gè)組,像1,9,8,7,6,8灵奖。9到6是降序嚼沿,為一個(gè)組,然后把降序的一組排成升序:1,6,7,8,9,8瓷患。然后最后的8后面繼續(xù)往后面找骡尽。。擅编。

每遇到這樣一個(gè)降序組攀细,++count,當(dāng)count大于MAX_RUN_COUNT(67)爱态,被判斷為這個(gè)數(shù)組不具備結(jié)構(gòu)(也就是這數(shù)據(jù)時(shí)而升時(shí)而降)谭贪,然后送給之前的sort(里面的快速排序)的方法(The array is not highly structured,use Quicksort instead of merge sort.)。

如果count少于MAX_RUN_COUNT(67)的锦担,說明這個(gè)數(shù)組還有點(diǎn)結(jié)構(gòu)俭识,就繼續(xù)往下走下面的歸并排序:

   // Determine alternation base for merge
    byte odd = 0;
    for (int n = 1; (n <<= 1) < count; odd ^= 1);

從這里開始,正式進(jìn)入歸并排序(Merge Sort)洞渔!

    // Merging
    for (int last; count > 1; count = last) {
        for (int k = (last = 0) + 2; k <= count; k += 2) {
            int hi = run[k], mi = run[k - 1];
            for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
                if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
                    b[i + bo] = a[p++ + ao];
                } else {
                    b[i + bo] = a[q++ + ao];
                }
            }
            run[++last] = hi;
        }
        if ((count & 1) != 0) {
            for (int i = right, lo = run[count - 1]; --i >= lo;
                b[i + bo] = a[i + ao]
            );
            run[++last] = right;
        }
        int[] t = a; a = b; b = t;
        int o = ao; ao = bo; bo = o;
    }
歸并排序(Merge Sort)

總結(jié):
從上面分析套媚,Arrays.sort并不是單一的排序缚态,而是插入排序,快速排序凑阶,歸并排序三種排序的組合猿规,為此我畫了個(gè)流程圖:


原創(chuàng)圖,轉(zhuǎn)發(fā)請(qǐng)標(biāo)明出處

算法的選擇:
PS:關(guān)于排序算法的文章宙橱,推薦這兩篇姨俩,個(gè)人覺得寫得挺好,容易入門:
https://mp.weixin.qq.com/s/t0dsJeN397wO41pwBWPeTg
https://www.cnblogs.com/huangbw/p/7398418.html

穩(wěn)定:如果a原本在b前面师郑,而a=b环葵,排序之后a仍然在b的前面;

不穩(wěn)定:如果a原本在b的前面宝冕,而a=b张遭,排序之后a可能會(huì)出現(xiàn)在b的后面;

時(shí)間復(fù)雜度按n越大算法越復(fù)雜來排的話:常數(shù)階O(1)地梨、對(duì)數(shù)階O(logn)菊卷、線性階O(n)、線性對(duì)數(shù)階O(nlogn)宝剖、平方階O(n2)洁闰、立方階O(n3)、……k次方階O(n的k次方)万细、指數(shù)階O(2的n次方)扑眉。


轉(zhuǎn)圖

O(nlogn)只代表增長量級(jí),同一個(gè)量級(jí)前面的常數(shù)也可以不一樣赖钞,不同數(shù)量下面的實(shí)際運(yùn)算時(shí)間也可以不一樣腰素。數(shù)量非常小的情況下(就像上面說到的,少于47的)雪营,插入排序等可能會(huì)比快速排序更快弓千。
所以數(shù)組少于47的會(huì)進(jìn)入插入排序。

快排數(shù)據(jù)越無序越快(加入隨機(jī)化后基本不會(huì)退化)卓缰,平均常數(shù)最小计呈,不需要額外空間,不穩(wěn)定排序征唬。
歸排速度穩(wěn)定捌显,常數(shù)比快排略大,需要額外空間总寒,穩(wěn)定排序扶歪。
所以大于或等于47或少于286會(huì)進(jìn)入快排,而在大于或等于286后,會(huì)有個(gè)小動(dòng)作:“// Check if the array is nearly sorted”善镰。這里第一個(gè)作用是先梳理一下數(shù)據(jù)方便后續(xù)的歸并排序妹萨,第二個(gè)作用就是即便大于286,但在降序組太多的時(shí)候(被判斷為沒有結(jié)構(gòu)的數(shù)據(jù)炫欺,The array is not highly structured,use Quicksort instead of merge sort.)乎完,要轉(zhuǎn)回快速排序。


這就是jdk8中Arrays.sort的底層原理品洛,自己在研究和分析中學(xué)到很多树姨,希望能給各位工作中或面試中一些啟發(fā)和幫助!Thanks for watching桥状!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帽揪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辅斟,更是在濱河造成了極大的恐慌转晰,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件士飒,死亡現(xiàn)場離奇詭異查邢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)酵幕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門侠坎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人裙盾,你說我怎么就攤上這事∷眨” “怎么了番官?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钢属。 經(jīng)常有香客問我徘熔,道長,這世上最難降的妖魔是什么淆党? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任酷师,我火速辦了婚禮,結(jié)果婚禮上染乌,老公的妹妹穿的比我還像新娘山孔。我一直安慰自己,他們只是感情好荷憋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布台颠。 她就那樣靜靜地躺著,像睡著了一般勒庄。 火紅的嫁衣襯著肌膚如雪串前。 梳的紋絲不亂的頭發(fā)上瘫里,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音荡碾,去河邊找鬼谨读。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坛吁,可吹牛的內(nèi)容都是我干的劳殖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼阶冈,長吁一口氣:“原來是場噩夢啊……” “哼闷尿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起女坑,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤填具,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后匆骗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劳景,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年碉就,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盟广。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瓮钥,死狀恐怖筋量,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碉熄,我是刑警寧澤桨武,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站锈津,受9級(jí)特大地震影響呀酸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜琼梆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一性誉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茎杂,春花似錦错览、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春悼粮,著一層夾襖步出監(jiān)牢的瞬間闲勺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國打工扣猫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菜循,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓申尤,卻偏偏與公主長得像癌幕,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昧穿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 概述 因?yàn)榻⊥自叮由蠈?duì)各種排序算法理解不深刻,過段時(shí)間面對(duì)排序就蒙了时鸵。所以決定對(duì)我們常見的這幾種排序算法進(jìn)行統(tǒng)一總...
    清風(fēng)之心閱讀 700評(píng)論 0 1
  • 排序算法 冒泡排序 選擇排序 插入排序 快速排序(最常見) 希爾排序 歸并排序 源碼:Sorting 冒泡排序 冒...
    廖少少閱讀 2,722評(píng)論 12 101
  • 排序算法說明 (1)排序的定義:對(duì)一序列對(duì)象根據(jù)某個(gè)關(guān)鍵字進(jìn)行排序胶逢; 輸入:n個(gè)數(shù):a1,a2,a3,…,an 輸...
    code武閱讀 662評(píng)論 0 0
  • 文章出處:原畫人 《絕地求生》太火了初坠。自推出...
    emotion者閱讀 968評(píng)論 0 0
  • 如果有一天 世界末日 彗星撞地球 我想我會(huì)第一時(shí)間去找你 接回家 和爸爸媽媽在一起 一家人吃一頓飯 身邊有小狗狗陪...
    我愛大白白閱讀 311評(píng)論 0 1