源碼系列(一)ArrayList 源碼解析

1秦叛、ArrayList定義

ArrayList 是一個(gè)數(shù)組隊(duì)列览露,相當(dāng)于 動(dòng)態(tài)數(shù)組判莉。與Java中的數(shù)組相比,它的容量能動(dòng)態(tài)增長(zhǎng)甲捏。它繼承于A(yíng)bstractList,實(shí)現(xiàn)了List, RandomAccess, Cloneable, java.io.Serializable這些接口鞭执。

ArrayList 繼承了AbstractList司顿,實(shí)現(xiàn)了List芒粹。它是一個(gè)數(shù)組隊(duì)列,提供了相關(guān)的添加大溜、刪除是辕、修改、遍歷等功能猎提。
ArrayList 實(shí)現(xiàn)了RandmoAccess接口,即提供了隨機(jī)訪(fǎng)問(wèn)功能旁蔼。RandmoAccess是java中用來(lái)被List實(shí)現(xiàn)锨苏,為L(zhǎng)ist提供快速訪(fǎng)問(wèn)功能的。在A(yíng)rrayList中棺聊,我們即可以通過(guò)元素的序號(hào)快速獲取元素對(duì)象伞租;這就是快速隨機(jī)訪(fǎng)問(wèn)。稍后限佩,我們會(huì)比較List的“快速隨機(jī)訪(fǎng)問(wèn)”和“通過(guò)Iterator迭代器訪(fǎng)問(wèn)”的效率葵诈。

ArrayList 實(shí)現(xiàn)了Cloneable接口,即覆蓋了函數(shù)clone()祟同,能被克隆作喘。

ArrayList 實(shí)現(xiàn)java.io.Serializable接口,這意味著ArrayList支持序列化晕城,能通過(guò)序列化去傳輸泞坦。

和Vector不同,ArrayList中的操作不是線(xiàn)程安全的砖顷!所以贰锁,建議在單線(xiàn)程中才使用ArrayList,而在多線(xiàn)程中可以選擇Vector或者CopyOnWriteArrayList滤蝠。

2豌熄、ArrayList源碼分析

ArrayList 有以下三種初始化的方法,此為1.8.0的源碼物咳,和之前的源碼有所區(qū)別锣险,在之前的源碼中,默認(rèn)情況下览闰,初始容量為10囱持,而在1.8的源碼中,默認(rèn)情況下則為

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
     * 指定一個(gè)初始容量的構(gòu)造方法
     *
     * @param  ArrayList的初始容量
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        //如果初始容量>0 焕济,則以指定的值為初始容量
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
      //如果指定的值為0纷妆,則直接賦值為空數(shù)組
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     *在此方法中則默認(rèn)賦值一個(gè)DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空數(shù)組,此數(shù)組和EMPTY_ELEMENTDATA 區(qū)分開(kāi)來(lái)晴弃,用來(lái)在添加第一個(gè)元素的時(shí)候掩幢,確定要擴(kuò)容多少
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     *構(gòu)造一個(gè)包含指定元素的list逊拍,這些元素的是按照Collection的迭代器返回的順序排列的
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            //如果是非空的集合,則直接拷貝進(jìn)入當(dāng)前數(shù)組
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 如果傳入的數(shù)據(jù)為空际邻,則使用默認(rèn)的空數(shù)組代替
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

3芯丧、增刪改查

add方法

 /**
     * 直接在集合的末尾添加一個(gè)元素
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

add方法分成了兩步進(jìn)行,

判斷并擴(kuò)充容量( ensureCapacityInternal(size + 1))+賦值( elementData[size++] = e)

從代碼中可以看出世曾,進(jìn)行添加數(shù)據(jù)操作的時(shí)候缨恒,會(huì)先判斷當(dāng)前底層的數(shù)組是否是DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,這個(gè)數(shù)組在構(gòu)造方法 ArrayList() 中被賦值轮听,用來(lái)區(qū)別于EMPTY_ELEMENTDATA 骗露,在第一次進(jìn)行數(shù)組操作的時(shí)候進(jìn)行判斷,從源碼可以看出血巍,會(huì)和默認(rèn)的容量10進(jìn)行比較萧锉,取較大的值,相對(duì)于之前直接 賦值為 10 的容量述寡,個(gè)人覺(jué)得有兩點(diǎn)優(yōu)勢(shì):1柿隙、初始化的時(shí)候不會(huì)直接開(kāi)辟空間 2、減少了一次擴(kuò)容操作鲫凶;效率上有一定的提升禀崖,設(shè)計(jì)還是比較巧妙的

private void ensureCapacityInternal(int minCapacity) {
       //如果調(diào)用的是 參數(shù)為空的構(gòu)造方法,第一次操作的時(shí)候螟炫,就判斷一下帆焕,
       //如果此時(shí)請(qǐng)求的容量大于默認(rèn)初始容量10,取請(qǐng)求的容量不恭,否則為默認(rèn)容量
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //明確容量
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 如果請(qǐng)求的最小容量大于數(shù)組長(zhǎng)度叶雹,則進(jìn)行一次擴(kuò)容,以?xún)?chǔ)存所有數(shù)據(jù)
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

//增加容量折晦,以確保能容納mincapacity指定的元素
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //擴(kuò)容,新的容量為原來(lái)的 1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //將老數(shù)據(jù)copy到新的數(shù)組中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

add 的其他方法

從源碼可以很明確的看出來(lái)满着,和add(E e)非常接近,僅在copy數(shù)據(jù)的時(shí)候有所差別

 public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //和add(E e)方法的區(qū)別僅這一行代碼贯莺,擴(kuò)容之后风喇,將老數(shù)據(jù)在從指定位置(index)開(kāi)始
      //之后的數(shù)據(jù)都向后移動(dòng)一位,然后將index位置的賦值為 e
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

//和add 方法類(lèi)似缕探,區(qū)別在于將傳入的集合轉(zhuǎn)化成數(shù)組魂莫,然后整體位移copy
 public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        //擴(kuò)容判斷
        ensureCapacityInternal(size + numNew);  // Increments modCount
      //將傳入的數(shù)據(jù)copy進(jìn)入數(shù)組
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

刪除 remove方法

移出數(shù)據(jù)有 remove(int index) remove(Object o) ,兩個(gè)方法本質(zhì)都是根據(jù)index爹耗,直接利用數(shù)組的下標(biāo)刪除指定數(shù)據(jù)耙考,同時(shí)會(huì)將數(shù)據(jù)進(jìn)行位移谜喊,填補(bǔ)空缺

 public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        E oldValue = (E) elementData[index];

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

 public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

查找 get(int index)

ArrayList底層是數(shù)組,因此查找就非常簡(jiǎn)單倦始,直接根據(jù)數(shù)組下標(biāo)找到數(shù)據(jù)

public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        return (E) elementData[index];
    }

更改 set(int index, E element)

更改也是非常簡(jiǎn)單斗遏,直接根據(jù)數(shù)組下標(biāo)更改數(shù)據(jù)

   public E set(int index, E element) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        E oldValue = (E) elementData[index];
        elementData[index] = element;
        return oldValue;
    }

增刪改查小結(jié)

從上面的源碼可以很明顯的看出,ArrayList的增刪改查操作實(shí)質(zhì)上就是對(duì)底層數(shù)組的操作鞋邑,新增的時(shí)候诵次,都需要對(duì)數(shù)組進(jìn)行擴(kuò)容+copy操作,刪除也需要對(duì)數(shù)組進(jìn)行copy枚碗,所以ArrayList的增加和刪除效率會(huì)非常低逾一,但是相對(duì)的,得利于底層的數(shù)組結(jié)果视译,在進(jìn)行查找和更改操作的時(shí)候,可以根據(jù)下標(biāo)直接進(jìn)行操作归敬,只有O(1)的復(fù)雜度酷含,因此在查找需求比較頻繁的操作中,可以使用ArrayList汪茧,極大的增加操作效率椅亚,但是在增刪比較頻繁的時(shí)候,就需要考慮其他的數(shù)據(jù)結(jié)構(gòu)了舱污;
同時(shí)呀舔,合理的使用ArrayList的構(gòu)造方法,例如在初始化的時(shí)候扩灯,如果已經(jīng)知道當(dāng)前數(shù)據(jù)的大小媚赖,可以直接使用ArrayList(int initialCapacity) 構(gòu)造方法指定初始容量,這樣可以避免在添加數(shù)據(jù)的時(shí)候頻繁擴(kuò)容降低性能珠插,同時(shí)也可以避免1.5倍擴(kuò)容機(jī)制造成的空間浪費(fèi)

最后編輯于
?著作權(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
  • 文/不壞的土叔 我叫張陵糊治,是天一觀(guān)的道長(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)容