讀 Java Arrays 源碼 筆記

Arrays.java是Java中用來(lái)操作數(shù)組的類(lèi)澡谭。使用這個(gè)工具類(lèi)可以減少平常很多的工作量诈嘿。了解其實(shí)現(xiàn)算谈,可以避免一些錯(cuò)誤的用法嘉冒。

它提供的操作包括:

  1. 排序 sort
  2. 查找 binarySearch()
  3. 比較 equals
  4. 填充 fill
  5. 轉(zhuǎn)列表 asList()
  6. 哈希 Hash()
  7. 轉(zhuǎn)字符串 toString()

這個(gè)類(lèi)的代碼量很多曹货,Java1.7中有4000多行。因?yàn)槊恳环N基本類(lèi)型都做了兼容讳推,所以整個(gè)類(lèi)真正邏輯不多顶籽。下面簡(jiǎn)單介紹一下它各個(gè)功能的實(shí)現(xiàn):

排序


這里的排序?qū)崿F(xiàn)有兩種

一種是為基本類(lèi)型數(shù)組設(shè)計(jì)的,它的對(duì)外接口有兩種银觅,如下:


//whole array
public static void sort(primitive[] a);

//sub array
public static void sort(primitive[] a, int fromIndex, int toIndex);

它們的具體實(shí)現(xiàn)方式是一樣的都是調(diào)用了DualPivotQuicksort.sort(...)方法礼饱。這個(gè)方法的中文含義是 雙軸快速排序。它在性能上優(yōu)于傳統(tǒng)的單軸快速排序究驴。

算法的邏輯可以參考國(guó)外一篇博客
如果想要閱讀源碼可以參考我的另一篇博客雙軸快速排序源碼閱讀筆記

它是不穩(wěn)定

另一種是為Object對(duì)象設(shè)計(jì)的镊绪,它要求傳進(jìn)來(lái)的數(shù)組對(duì)象必須實(shí)現(xiàn)Comparable接口。
它提供的接口如下:


// whole array, default asec
public static void sort(Object[] a);

// subArray, default asec
public static void sort(Object[] a, int fromIndex, int toIndex);

還有帶泛型參數(shù)的接口洒忧,它需要指定一個(gè)比較器蝴韭。

// whole array with comparator
public static <T> void sort(T[] a, Comparator<? super T> c);

// sub array with comparator
public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c);

他的實(shí)現(xiàn)方式如下:

// java/utils/Arrays.java
static final class LegacyMergeSort {
        private static final boolean userRequested =
            java.security.AccessController.doPrivileged(
                new sun.security.action.GetBooleanAction(
                    "java.util.Arrays.useLegacyMergeSort")).booleanValue();
}

//sort 方法的實(shí)現(xiàn)
public static void sort(Object[] a) {
    if (LegacyMergeSort.userRequested)
       legacyMergeSort(a);
    else
       //與TimSort的邏輯是相同的
       ComparableTimSort.sort(a);
}

//legacyMergeSort
private static void legacyMergeSort(Object[] a) {
    Object[] aux = a.clone();
    mergeSort(aux, a, 0, a.length, 0);
}                    

//歸并排序
    private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        // 小數(shù)組直接進(jìn)行普通插入排序
        if (length < INSERTIONSORT_THRESHOLD) {
             ///...
            return;
        }

        //下面是歸并排序的實(shí)現(xiàn),
        ///...
    }

從上面的邏輯可以看出來(lái)熙侍,它的實(shí)現(xiàn)方式分為兩種万皿,一種是通過(guò)Arrays.java中的歸并排序?qū)崿F(xiàn)的摧找,另一種采用了TimSort算法。其中Arrays.java中的歸并排序的邏輯相對(duì)簡(jiǎn)單牢硅,是一種插入排序與傳統(tǒng)歸并排序的結(jié)合蹬耘。當(dāng)待排序的數(shù)組小于INSERTIONSROT_THERSHOLD的時(shí)候直接進(jìn)行插入排序,不再遞歸减余。TimSort算法也是一種插入排序與歸并排序結(jié)合的算法综苔,不過(guò)它的細(xì)節(jié)優(yōu)化要比Arrays.java中的算法做的多。詳細(xì)介紹可以參考維基百科或者我的TimSort 源碼筆記位岔。

兩種算法的切換依靠運(yùn)行時(shí)系統(tǒng)變量的設(shè)置如筛。具體參考StackOverFlow上的一篇回答。我們默認(rèn)情況下是不打開(kāi)這個(gè)開(kāi)關(guān)的抒抬,也就是說(shuō)沒(méi)有特殊要求的情況下杨刨,我們默認(rèn)使用TimSort算法來(lái)實(shí)現(xiàn)排序。從注釋上來(lái)看擦剑,在未來(lái)某個(gè)版本妖胀,Arrays.java中的merge方法將會(huì)被刪除掉。

這個(gè)排序方法是 穩(wěn)定 的惠勒。

查找

Arrays.java中只提供了二分查找赚抡。二分查找的前提就是數(shù)組是經(jīng)過(guò)升序排序的,所以使用之前務(wù)必確保數(shù)組是有序的纠屋。

調(diào)用的接口比較簡(jiǎn)單:

public static int binarySearch(primative[] a, primative key);
public static int binarySearch(primative[] a, int fromIndex, int toIndex, primative key);
public static int binarySearch(Object[] a, Object key);
public static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key);
public static <T> int binarySearch(T[] a, T key, Comparator< ? super T> c);
public static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key,Comparator<? super T> c);

equals

這個(gè)也比較簡(jiǎn)單涂臣,equals方法與==的不同大家應(yīng)該都很熟悉了,這里直接貼出接口:

    // 基本類(lèi)型
    public static boolean equals(long[] a, long[] a2) {
        //...
    }
    // 對(duì)象
    public static boolean equals(Object[] a, Object[] a2) {
        //...
    }
    // 高維數(shù)組的equal比較售担,通過(guò)遞歸實(shí)現(xiàn)
    // 這里沒(méi)有對(duì)循環(huán)引用進(jìn)行檢查赁遗,如果兩個(gè)如組同時(shí)存在循環(huán)引用的情況下可能出現(xiàn)死循環(huán)的風(fēng)險(xiǎn)。
    public static boolean deepEquals(Object[] a1, Object[] a2) {
       //...
    }

fill 填充


批量初始化的時(shí)候不要自己寫(xiě)for循環(huán)了族铆,已經(jīng)有人幫我們寫(xiě)好了吼和。

// 基本類(lèi)型批量賦值
public static void fill(int[] a, int fromIndex, int toIndex, int val) {
    rangeCheck(a.length, fromIndex, toIndex);
    for (int i = fromIndex; i < toIndex; i++)
        a[i] = val;
}
// 對(duì)象批量賦值
public static void fill(Object[] a, int fromIndex, int toIndex, Object val) {
    rangeCheck(a.length, fromIndex, toIndex);
    for (int i = fromIndex; i < toIndex; i++)
        a[i] = val;
}

復(fù)制


數(shù)組復(fù)制的最終實(shí)現(xiàn)是調(diào)用了JVM中的方法。具體沒(méi)有深究骑素,但在數(shù)據(jù)量大的時(shí)候應(yīng)該能快些炫乓。


// @file Arrays.java
// 基本類(lèi)型的復(fù)制,從0開(kāi)始到指定長(zhǎng)度
public static byte[] copyOf(byte[] original, int newLength) {
    byte[] copy = new byte[newLength];
    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    return copy;
}
// 基本類(lèi)型復(fù)制献丑,從指定起點(diǎn)到指定終點(diǎn)
public static byte[] copyOfRange(byte[] original, int from, int to) {
    int newLength = to - from;
    if (newLength < 0)
        throw new IllegalArgumentException(from + " > " + to);
    byte[] copy = new byte[newLength];
    System.arraycopy(original, from, copy, 0,
                    Math.min(original.length - from, newLength));
    return copy;
}
//這里是泛型數(shù)組的復(fù)制末捣, 結(jié)合泛型進(jìn)階中的內(nèi)容,這里好理解很多创橄。
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

// @file System.java
public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

// 

轉(zhuǎn)換成列表 asList


將一組對(duì)象轉(zhuǎn)換成列表箩做,這里一定要注意返回的ArrayList并非平常用的java.util.ArrayList ,而是Arrays.java中定義的一個(gè)簡(jiǎn)單的靜態(tài)內(nèi)部類(lèi)--ArrayList妥畏。它不支持添加和移除元素邦邦,不支持?jǐn)U容安吁。

@file java/util/Arrays.java

@SafeVarargs
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

//注意,此ArrayList非平常用的ArrayList;這里的實(shí)現(xiàn)比較簡(jiǎn)單燃辖。
//不能擴(kuò)容鬼店,所以不支持add,remove等操作。
private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable {
    /// ...
}

哈希 hash

高維數(shù)組的哈希計(jì)算黔龟,采用遞歸實(shí)現(xiàn)妇智。同樣,如果自己的某個(gè)元素直接或者間接持有自己氏身,會(huì)出現(xiàn)死循環(huán)巍棱。

    // 基本類(lèi)型哈希
    public static int hashCode(long a[]) {
       // ...
    }
    
    // 對(duì)象哈希
    public static int hashCode(Object a[]) {
       //...
    }
    
    // 高維數(shù)組哈希,采用遞歸實(shí)現(xiàn)蛋欣。如果自己的某個(gè)元素直接或者間接持有自己航徙,會(huì)出現(xiàn)死訊環(huán),
    // 所以`Object[]`最好直接使用`hashcode(Object)`陷虎。
    public static int deepHashCode(Object a[]) {
       //...
    }

toString


有了這個(gè)方法到踏,打Log的時(shí)候就不需要寫(xiě)循環(huán)了。
這里的高維數(shù)組直接或者間接持有自己的情況不會(huì)出現(xiàn)死循環(huán)泻红。因?yàn)檫@里做了特殊處理夭禽,用一個(gè)Set保存了打印過(guò)的數(shù)組霞掺。

    // 基本類(lèi)型
    public static String toString(int[] a) {
       // ...
    }
    // 對(duì)象
    public static String toString(Object[] a) {
        // ...
    }
    // 高維數(shù)組toString(). 這里處理了自我持有的問(wèn)題谊路。
    public static String deepToString(Object[] a) {
        // ...
        deepToString(a, buf, new HashSet<Object[]>());
        return buf.toString();
    }
    // 真正的實(shí)現(xiàn)方法, 遞歸實(shí)現(xiàn)菩彬。
    // 使用了一個(gè)Set來(lái)存儲(chǔ)已經(jīng)打印過(guò)的類(lèi)缠劝,如果再次出現(xiàn)這個(gè)對(duì)象,直接打印[...]
    private static void deepToString(Object[] a, StringBuilder buf,
                                     Set<Object[]> dejaVu) {
       //...
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末骗灶,一起剝皮案震驚了整個(gè)濱河市惨恭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耙旦,老刑警劉巖脱羡,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異免都,居然都是意外死亡锉罐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)绕娘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脓规,“玉大人,你說(shuō)我怎么就攤上這事险领∏扔撸” “怎么了秒紧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)挨下。 經(jīng)常有香客問(wèn)我熔恢,道長(zhǎng),這世上最難降的妖魔是什么复颈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任绩聘,我火速辦了婚禮,結(jié)果婚禮上耗啦,老公的妹妹穿的比我還像新娘凿菩。我一直安慰自己,他們只是感情好帜讲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布衅谷。 她就那樣靜靜地躺著,像睡著了一般似将。 火紅的嫁衣襯著肌膚如雪获黔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天在验,我揣著相機(jī)與錄音玷氏,去河邊找鬼。 笑死腋舌,一個(gè)胖子當(dāng)著我的面吹牛盏触,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播块饺,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赞辩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了授艰?” 一聲冷哼從身側(cè)響起辨嗽,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淮腾,沒(méi)想到半個(gè)月后糟需,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谷朝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年洲押,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徘禁。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诅诱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出送朱,到底是詐尸還是另有隱情娘荡,我是刑警寧澤干旁,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站炮沐,受9級(jí)特大地震影響争群,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜大年,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一换薄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翔试,春花似錦轻要、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至壁涎,卻和暖如春凡恍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怔球。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工嚼酝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人竟坛。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓闽巩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親流码。 傳聞我的和親對(duì)象是個(gè)殘疾皇子又官,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法延刘,類(lèi)相關(guān)的語(yǔ)法漫试,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法碘赖,異常的語(yǔ)法驾荣,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,641評(píng)論 18 399
  • 面向?qū)ο笾饕槍?duì)面向過(guò)程。 面向過(guò)程的基本單元是函數(shù)普泡。 什么是對(duì)象:EVERYTHING IS OBJECT(萬(wàn)物...
    sinpi閱讀 1,057評(píng)論 0 4
  • 一撼班、基本數(shù)據(jù)類(lèi)型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對(duì)于byte類(lèi)型而言...
    龍貓小爺閱讀 4,265評(píng)論 0 16
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等歧匈,對(duì)于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,501評(píng)論 0 3
  • 姑且叫小二吧,不知道應(yīng)該用第一人還是第二人稱來(lái)寫(xiě) 但我應(yīng)該寫(xiě)點(diǎn)什么 心中有丘壑 砰嘁,要讓我表達(dá)出來(lái)吧件炉,可真當(dāng)打字...
    灬文和灬閱讀 674評(píng)論 2 51