源碼解析(JDK1.8)之——ArrayList

1 ArrayList

1.1 底層結(jié)構(gòu)
底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組铺敌,數(shù)組元素類型為Object類型踢涌,即可以存放所有類型數(shù)據(jù)黔龟。

ArrayList的數(shù)據(jù)結(jié)構(gòu)

1.2 優(yōu)缺點(diǎn)
動(dòng)態(tài)數(shù)組實(shí)現(xiàn)历等,查找快讨惩,增刪慢

2 四個(gè)關(guān)注點(diǎn)

關(guān)注點(diǎn) 結(jié)論
ArrayList是否允許空 允許
ArrayList是否允許重復(fù)數(shù)據(jù) 允許
ArrayList是否有序 有序
ArrayList是否線程安全 非線程安全

3 ArrayList源碼解析

3.1 類的繼承關(guān)系

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

說(shuō)明:ArrayList繼承AbstractList抽象父類,實(shí)現(xiàn)了List接口(規(guī)定了List的操作規(guī)范)募闲、RandomAccess(可隨機(jī)訪問(wèn))步脓、Cloneable(可拷貝)、Serializable(可序列化)浩螺。

3.2 類的屬性

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 版本號(hào)
    private static final long serialVersionUID = 8683452581122892189L;
    // 缺省容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空對(duì)象數(shù)組
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 缺省空對(duì)象數(shù)組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 元素?cái)?shù)組
    transient Object[] elementData;
    // 實(shí)際元素大小靴患,默認(rèn)為0
    private int size;
    // 最大數(shù)組容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

說(shuō)明:類的屬性中核心的屬性為elementData,類型為Object[]要出,用于存放實(shí)際元素鸳君,并且被標(biāo)記為transient,也就意味著在序列化的時(shí)候患蹂,此字段是不會(huì)被序列化的或颊。

擴(kuò)展:為什么ArrayList的elementData是用transient修飾的?
看到ArrayList實(shí)現(xiàn)了Serializable接口传于,這意味著ArrayList是可以被序列化的囱挑,用transient修飾elementData意味著不希望elementData數(shù)組被序列化。這是為什么沼溜?因?yàn)樾蛄谢疉rrayList的時(shí)候平挑,ArrayList里面的elementData未必是滿的,比方說(shuō)elementData有10的大小系草,但是我只用了其中的3個(gè)通熄,那么是否有必要序列化整個(gè)elementData呢?顯然沒(méi)有這個(gè)必要找都,因此ArrayList中重寫了writeObject方法唇辨。每次序列化的時(shí)候調(diào)用這個(gè)方法,先調(diào)用defaultWriteObject()方法序列化ArrayList中的非transient元素能耻,elementData不去序列化它赏枚,然后遍歷elementData亡驰,只序列化那些有的元素,這樣:
1嗡贺、加快了序列化的速度
2隐解、減小了序列化之后的文件大小
不失為一種聰明的做法,如果以后開發(fā)過(guò)程中有遇到這種情況诫睬,也是值得學(xué)習(xí)煞茫、借鑒的一種思路。

3.3 類的構(gòu)造函數(shù)

1. ArrayList(int)型構(gòu)造函數(shù)

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { // 初始容量大于0
            this.elementData = new Object[initialCapacity]; // 根據(jù)初始容量初始化元素?cái)?shù)組
        } else if (initialCapacity == 0) { // 初始容量為0
            this.elementData = EMPTY_ELEMENTDATA; // 為空對(duì)象數(shù)組
        } else { // 初始容量小于0摄凡,拋出異常
            throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
        }
}

說(shuō)明:指定elementData數(shù)組的大小续徽,不允許初始化容量小于0,否則拋出異常亲澡。

2. ArrayList()型構(gòu)造函數(shù)

public ArrayList() { 
        // 無(wú)參構(gòu)造函數(shù)钦扭,設(shè)置元素?cái)?shù)組為空 
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

說(shuō)明:當(dāng)未指定初始化容量時(shí),會(huì)給elementData賦值為空集合床绪。

3. ArrayList(Collection<? extends E>)型構(gòu)造函數(shù)

public ArrayList(Collection<? extends E> c) { // 集合參數(shù)構(gòu)造函數(shù)
        elementData = c.toArray(); // 將集合轉(zhuǎn)化為數(shù)組
        if ((size = elementData.length) != 0) { // 集合非空
            if (elementData.getClass() != Object[].class) // 是否成功轉(zhuǎn)化為Object類型數(shù)組
                elementData = Arrays.copyOf(elementData, size, Object[].class); // 不為Object數(shù)組的話就進(jìn)行復(fù)制
        } else { // 集合大小為空客情,則設(shè)置元素?cái)?shù)組為空
            this.elementData = EMPTY_ELEMENTDATA;
        }
}

說(shuō)明:當(dāng)傳遞的參數(shù)為集合類型時(shí),會(huì)把集合類型轉(zhuǎn)化為數(shù)組類型癞己,并賦值給elementData膀斋。

3.4 核心函數(shù)分析

1.add函數(shù)

public boolean add(E e) { // 添加元素
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
}

說(shuō)明:在add函數(shù)我們發(fā)現(xiàn)還有其他的函數(shù)ensureCapacityInternal,此函數(shù)可以理解為確保elementData數(shù)組有合適的大小痹雅。ensureCapacityInternal的具體函數(shù)如下

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 判斷元素?cái)?shù)組是否為空數(shù)組
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); // 比較初始容量(10)和最小容量仰担,取較大值
        }
        
        ensureExplicitCapacity(minCapacity);
}

說(shuō)明:在ensureCapacityInternal函數(shù)中我們又發(fā)現(xiàn)了ensureExplicitCapacity函數(shù),這個(gè)函數(shù)也是為了確保elemenData數(shù)組有合適的大小绩社。ensureExplicitCapacity的具體函數(shù)如下

private void ensureExplicitCapacity(int minCapacity) {
        // 結(jié)構(gòu)性修改加1
        modCount++;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

說(shuō)明:在ensureExplicitCapacity函數(shù)我們又發(fā)現(xiàn)了grow函數(shù)摔蓝,grow函數(shù)才會(huì)對(duì)數(shù)組進(jìn)行擴(kuò)容,ensureCapacityInternal愉耙、ensureExplicitCapacity都只是過(guò)程贮尉,最后完成實(shí)際擴(kuò)容操作還是得看grow函數(shù),grow函數(shù)的具體函數(shù)如下

private void grow(int minCapacity) {
        int oldCapacity = elementData.length; // 舊容量
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量為舊容量的1.5倍
        if (newCapacity - minCapacity < 0) // 新容量小于參數(shù)指定容量朴沿,修改新容量
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) // 新容量大于最大容量
            newCapacity = hugeCapacity(minCapacity); // 指定新容量
        // 拷貝擴(kuò)容
        elementData = Arrays.copyOf(elementData, newCapacity);
}

說(shuō)明:正常情況下會(huì)擴(kuò)容1.5倍猜谚,特殊情況下(新擴(kuò)展數(shù)組大小已經(jīng)達(dá)到了最大值)則只取最大值。

當(dāng)我們調(diào)用add方法時(shí)悯仙,實(shí)際上的函數(shù)調(diào)用如下


add函數(shù)調(diào)用過(guò)程

示例一(調(diào)用無(wú)參構(gòu)造函數(shù))

List<Integer> lists = new ArrayList<Integer>();
lists.add(8);

說(shuō)明:初始化lists大小為0龄毡,調(diào)用的ArrayList()型構(gòu)造函數(shù)吠卷,那么在調(diào)用lists.add(8)方法時(shí)锡垄,會(huì)經(jīng)過(guò)怎樣的步驟呢?下圖給出了該程序執(zhí)行過(guò)程和最初與最后的elementData的大小


函數(shù)調(diào)用過(guò)程

說(shuō)明:我們可以看到祭隔,在add方法之前開始elementData = {}货岭;調(diào)用add方法時(shí)會(huì)繼續(xù)調(diào)用路操,直至grow,最后elementData的大小變?yōu)?0千贯,之后再返回到add函數(shù)屯仗,把8放在elementData[0]中。

示例二(調(diào)用有參構(gòu)造函數(shù):指定初始容量)

List<Integer> lists = new ArrayList<Integer>(6);
lists.add(8);

說(shuō)明:調(diào)用的ArrayList(int)型構(gòu)造函數(shù)搔谴,那么elementData被初始化為大小為6的Object數(shù)組魁袜,在調(diào)用add(8)方法時(shí),具體的步驟如下:


函數(shù)調(diào)用過(guò)程

說(shuō)明:相比上一個(gè)敦第,缺少一步grow()峰弹。因?yàn)檫M(jìn)行 if (minCapacity - elementData.length > 0) 判斷時(shí),minCapacity為1芜果, elementData.length為6鞠呈,條件為false,因此不會(huì)調(diào)用grow(),不會(huì)進(jìn)行擴(kuò)容處理右钾。

2.set函數(shù)

public E set(int index, E element) {
        // 檢驗(yàn)索引是否合法
        rangeCheck(index);
        // 舊值
        E oldValue = elementData(index);
        // 賦新值
        elementData[index] = element;
        // 返回舊值
        return oldValue;
}

3.indexOf函數(shù)

// 從首開始查找數(shù)組里面是否存在指定元素
public int indexOf(Object o) {
        if (o == null) { // 查找的元素為空
            for (int i = 0; i < size; i++) // 遍歷數(shù)組蚁吝,找到第一個(gè)為空的元素,返回下標(biāo)
                if (elementData[i]==null)
                    return i;
        } else { // 查找的元素不為空
            for (int i = 0; i < size; i++) // 遍歷數(shù)組舀射,找到第一個(gè)和指定元素相等的元素窘茁,返回下標(biāo)
                if (o.equals(elementData[i]))
                    return i;
        } 
        // 沒(méi)有找到,返回空
        return -1;
}

說(shuō)明:從頭開始查找與指定元素相等的元素后控,注意庙曙,是可以查找null元素的,意味著ArrayList中可以存放null元素的浩淘。與此函數(shù)對(duì)應(yīng)的lastIndexOf捌朴,表示從尾部開始查找。

4.get函數(shù)

public E get(int index) {
        // 檢驗(yàn)索引是否合法
        rangeCheck(index);

        return elementData(index);
}

5.remove函數(shù)

public E remove(int index) {
        // 檢查索引是否合法
        rangeCheck(index);
        
        modCount++;
        E oldValue = elementData(index);
        // 需要移動(dòng)的元素的個(gè)數(shù)
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 賦值為空张抄,有利于進(jìn)行GC
        elementData[--size] = null; 
        // 返回舊值
        return oldValue;
}

說(shuō)明:remove函數(shù)用戶移除指定下標(biāo)的元素砂蔽,此時(shí)會(huì)把指定下標(biāo)到數(shù)組末尾的元素向前移動(dòng)一個(gè)單位,并且會(huì)把數(shù)組最后一個(gè)元素設(shè)置為null署惯,這樣是為了方便之后將整個(gè)數(shù)組不被使用時(shí)左驾,會(huì)被GC,可以作為小的技巧使用极谊。


刪除下標(biāo)為2的過(guò)程示意圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诡右,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子轻猖,更是在濱河造成了極大的恐慌帆吻,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咙边,死亡現(xiàn)場(chǎng)離奇詭異猜煮,居然都是意外死亡次员,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門王带,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)淑蔚,“玉大人,你說(shuō)我怎么就攤上這事愕撰∩采溃” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵搞挣,是天一觀的道長(zhǎng)绪妹。 經(jīng)常有香客問(wèn)我,道長(zhǎng)柿究,這世上最難降的妖魔是什么邮旷? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮蝇摸,結(jié)果婚禮上婶肩,老公的妹妹穿的比我還像新娘。我一直安慰自己貌夕,他們只是感情好律歼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啡专,像睡著了一般险毁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上们童,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天畔况,我揣著相機(jī)與錄音,去河邊找鬼慧库。 笑死跷跪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的齐板。 我是一名探鬼主播吵瞻,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼甘磨!你這毒婦竟也來(lái)了橡羞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤济舆,失蹤者是張志新(化名)和其女友劉穎卿泽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吗冤,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡又厉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了椎瘟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片覆致。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肺蔚,靈堂內(nèi)的尸體忽然破棺而出煌妈,到底是詐尸還是另有隱情,我是刑警寧澤宣羊,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布璧诵,位于F島的核電站,受9級(jí)特大地震影響仇冯,放射性物質(zhì)發(fā)生泄漏之宿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一苛坚、第九天 我趴在偏房一處隱蔽的房頂上張望比被。 院中可真熱鬧,春花似錦泼舱、人聲如沸等缀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尺迂。三九已至,卻和暖如春冒掌,著一層夾襖步出監(jiān)牢的瞬間噪裕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工股毫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留州疾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓皇拣,卻偏偏與公主長(zhǎng)得像严蓖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子氧急,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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