ArrayList內(nèi)部原理解析

博文出處:ArrayList內(nèi)部原理解析炬称,歡迎大家關(guān)注我的博客塌西,謝謝蛙紫!

注:本文解析的 ArrayList 源代碼基于 Java 1.8 皂股。

Header

之前講了 HashMap 的原理后劈猪,今天來看一下 ArrayList 昧甘。

ArrayList 也是非常常用的集合類。它是有序的并且可以存儲(chǔ)重復(fù)元素的战得。 ArrayList 底層其實(shí)就是一個(gè)數(shù)組充边,并且會(huì)動(dòng)態(tài)擴(kuò)容的。

源碼分析

構(gòu)造方法

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 創(chuàng)建初始容量的數(shù)組
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

public ArrayList() {
    // 默認(rèn)為空數(shù)組
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // 將集合中的元素復(fù)制到數(shù)組中
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

構(gòu)造方法中的代碼比較簡短常侦,大家都能理解的吧浇冰。

add()

    public boolean add(E e) {
        // 確保數(shù)組的容量,保證可以添加該元素
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 將該元素放入數(shù)組中
        elementData[size++] = e;
        return true;
    }

發(fā)現(xiàn)在 add() 方法中聋亡,代碼很簡短肘习。可以看出之前的預(yù)操作都放入了 ensureCapacityInternal 方法中坡倔,這個(gè)方法會(huì)去確保該元素在數(shù)組中有位置可以放入漂佩。

那么我們來看看這個(gè)方法:

    private void ensureCapacityInternal(int minCapacity) {
        // 如果數(shù)組是空的,那么會(huì)初始化該數(shù)組
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // DEFAULT_CAPACITY 為 10 罪塔,所以調(diào)用無參默認(rèn) ArrayList 構(gòu)造方法初始化的話投蝉,默認(rèn)的數(shù)組容量為 10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    
        ensureExplicitCapacity(minCapacity);
    }

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

        // 確保數(shù)組的容量,如果不夠的話征堪,調(diào)用 grow 方法擴(kuò)容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

看了半天瘩缆,擴(kuò)容是在 grow 方法中完成的,所以我們接著跟進(jìn)佃蚜。

    private void grow(int minCapacity) {
        // 當(dāng)前數(shù)組的容量
        int oldCapacity = elementData.length;
        // 新數(shù)組擴(kuò)容為原來容量的 1.5 倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果新數(shù)組擴(kuò)容容量還是比最少需要的容量還要小的話庸娱,就設(shè)置擴(kuò)充容量為最小需要的容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //判斷新數(shù)組容量是否已經(jīng)超出最大數(shù)組范圍,MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // 復(fù)制元素到新的數(shù)組中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

擴(kuò)容方法其實(shí)就是新創(chuàng)建一個(gè)數(shù)組爽锥,然后將舊數(shù)組的元素都復(fù)制到新數(shù)組里面。

當(dāng)然畔柔,add 還有一個(gè)重載的方法 add(int index, E element) 氯夷,順便我們也來看一下。

    public void add(int index, E element) {
        // 判斷 index 有沒有超出索引的范圍
        rangeCheckForAdd(index);
        // 和之前的操作是一樣的靶擦,都是保證數(shù)組的容量足夠
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 將指定位置及其后面數(shù)據(jù)向后移動(dòng)一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 將該元素添加到指定的數(shù)組位置
        elementData[index] = element;
        // ArrayList 的大小改變
        size++;
    }

好了腮考,add 方法看的差不多了雇毫,剩下還有一個(gè) addAll(Collection<? extends E> c) 方法也是換湯不換藥的,可以自己回去看下踩蔚,這里就不講了棚放。

get()

get 方法很簡單,就是在數(shù)組中返回指定位置的元素即可馅闽。

    public E get(int index) {
        // 檢查 index 有沒有超出索引的范圍
        rangeCheck(index);
        // 返回指定位置的元素
        return elementData(index);
    }

remove()

    public E remove(int index) {
        // 檢查 index 有沒有超出索引的范圍
        rangeCheck(index);

        modCount++;
        // 保存一下需要?jiǎng)h除的數(shù)據(jù)
        E oldValue = elementData(index);
        // 讓指定刪除的位置后面的數(shù)據(jù)飘蚯,向前移動(dòng)一位
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 方便 gc 釋放內(nèi)存
        elementData[--size] = null; // clear to let GC do its work
        // 返回舊值
        return oldValue;
    }

remove 中主要是將之后的元素都向前一位移動(dòng),然后將最后一位的值設(shè)置為空福也。最后局骤,返回已經(jīng)刪除的值。

同樣暴凑,remove 還有一個(gè)重載的方法 remove(Object o) 峦甩。

    public boolean remove(Object o) {
        if (o == null) {
            // 如果有元素的值為 null 的話,移除該元素现喳,fastRemove 的操作和上面的 remove(int index) 是類似的
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            // 如果有元素的值等于 o 的話凯傲,移除該元素
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

clear()

clear 方法無非就是遍歷數(shù)組,然后把所有的值都置為 null 嗦篱。

    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

Footer

至此冰单,ArrayList 主要的幾個(gè)方法就講完了。ArrayList 的源碼還是比較簡單的默色,基本上都可以看得明白球凰。

我們來總結(jié)一下:

  1. ArrayList底層是基于數(shù)組來實(shí)現(xiàn)的,因此在 get 的時(shí)候效率高腿宰,而 add 或者 remove 的時(shí)候呕诉,效率低;
  2. 調(diào)用默認(rèn)的 ArrayList 無參構(gòu)造方法的話吃度,數(shù)組的初始容量為 10 甩挫;
  3. ArrayList 會(huì)自動(dòng)擴(kuò)容,擴(kuò)容的時(shí)候椿每,會(huì)將容量擴(kuò)至原來的 1.5 倍伊者;
  4. ArrayList 不是線程安全的;

那么今天就這樣了间护,之后有空給大家講講 LinkedList 亦渗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市汁尺,隨后出現(xiàn)的幾起案子法精,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搂蜓,死亡現(xiàn)場離奇詭異狼荞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帮碰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門相味,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人殉挽,你說我怎么就攤上這事丰涉。” “怎么了此再?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵昔搂,是天一觀的道長。 經(jīng)常有香客問我输拇,道長摘符,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任策吠,我火速辦了婚禮逛裤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猴抹。我一直安慰自己带族,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布蟀给。 她就那樣靜靜地躺著蝙砌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跋理。 梳的紋絲不亂的頭發(fā)上择克,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天前普,我揣著相機(jī)與錄音,去河邊找鬼拭卿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛峻厚,可吹牛的內(nèi)容都是我干的响蕴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惠桃,長吁一口氣:“原來是場噩夢啊……” “哼懊渡!你這毒婦竟也來了军拟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤誓禁,失蹤者是張志新(化名)和其女友劉穎懈息,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摹恰,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年姑宽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炮车。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酣溃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赊豌,到底是詐尸還是另有隱情,我是刑警寧澤碘饼,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站住涉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秆吵。R本人自食惡果不足惜五慈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泻拦。 院中可真熱鬧,春花似錦争拐、人聲如沸晦雨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽展辞。三九已至,卻和暖如春罗珍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背覆旱。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留藕坯,地道東北人噪沙。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像曲聂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子朋腋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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

  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等旭咽,對于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,500評(píng)論 0 3
  • 一.線性表 定義:零個(gè)或者多個(gè)元素的有限序列。也就是說它得滿足以下幾個(gè)條件:??①該序列的數(shù)據(jù)元素是有限的轿塔。??②...
    Geeks_Liu閱讀 2,701評(píng)論 1 12
  • 文章有點(diǎn)長仲墨,比較啰嗦,請耐心看完D垦(基于Android API 25) 一、概述 首先得明白ArrayList在數(shù)...
    JerryloveEmily閱讀 3,199評(píng)論 2 15
  • 最近在惡補(bǔ)數(shù)據(jù)結(jié)構(gòu)和算法幻梯,順帶著把ArrayList和Vector看了看兜畸,因此寫了這篇文章記錄下碘梢。我們知道Arra...
    Zeit丶閱讀 2,445評(píng)論 0 4
  • 呃 其實(shí)是昨天追太陽的后裔和銀魂搞得第二天的內(nèi)容沒寫完。最近好虐 國足出線 銀魂本季完結(jié) 心塞...不過每天洗完澡...
    QJK閱讀 362評(píng)論 0 0