ArrayList源碼記錄

原文地址:https://xeblog.cn/articles/19

本文主要介紹的是ArrayList在JDK8中的實(shí)現(xiàn)。

ArrayList簡(jiǎn)介

ArrayList 是一個(gè)數(shù)組隊(duì)列蜘拉,內(nèi)部維護(hù)一個(gè)Java數(shù)組泊交,并且它是動(dòng)態(tài)的祈噪,數(shù)組的容量可以自動(dòng)增長(zhǎng)坟募。它繼承了AbstractList,且實(shí)現(xiàn)了List嗓节、RandomAccess勿锅、Cloneable帕膜、Serializable等接口。

ArrayList UML

ArrayList的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 支持隨機(jī)存取溢十,由于其內(nèi)部是一個(gè)數(shù)組垮刹,隨機(jī)訪問(wèn)元素等于通過(guò)數(shù)組下標(biāo)訪問(wèn),隨機(jī)獲取元素效率高张弛。
  • 元素是有序的(按照添加順序)荒典。
  • 支持自動(dòng)擴(kuò)容(既是優(yōu)點(diǎn)也是缺點(diǎn))酪劫。

缺點(diǎn)

  • 線程不安全。
  • 自動(dòng)擴(kuò)容效率低寺董,每次擴(kuò)容都需要將所有元素添加到新增數(shù)組中覆糟。
  • 添加和刪除操作需要移動(dòng)數(shù)組中的元素。

疑問(wèn):為何ArrayList繼承AbstractList后又實(shí)現(xiàn)了List接口遮咖?

image
  • 方便使用Java反射機(jī)制獲取方法實(shí)現(xiàn)的所有接口陶珠,因?yàn)槿绻伙@式的實(shí)現(xiàn)List接口挟裂,反射獲取接口的時(shí)候還需要先獲取父類,然后再通過(guò)父類獲取接口揍诽。
  • 方便一眼就能看出ArrayList實(shí)現(xiàn)了List接口(敷衍.gif)诀蓉。
  • 其它...

ArrayList的部分字段

/**
 * 默認(rèn)容量為10
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 空數(shù)組 
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 用于默認(rèn)大小的空數(shù)組。將此與EMPTY_ELEMENTDATA區(qū)分開(kāi)來(lái)暑脆,以便在添加第一個(gè)元素時(shí)知道要膨脹多少渠啤。
 * 使用無(wú)參構(gòu)造初始化ArrayList時(shí)默認(rèn)的數(shù)組
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存儲(chǔ)ArrayList元素的數(shù)組。添加第一個(gè)元素時(shí)饵筑,如果elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * 則此數(shù)組的長(zhǎng)度是默認(rèn)的10
 */
transient Object[] elementData;

/**
 * ArrayList的元素個(gè)數(shù)
 */
private int size;

ArrayList的構(gòu)造方法

帶一個(gè)int類型參數(shù)的構(gòu)造方法埃篓,傳入的是ArrayList的初始長(zhǎng)度

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
            // 默認(rèn)的空數(shù)組 Object[] EMPTY_ELEMENTDATA = {}
            this.elementData = EMPTY_ELEMENTDATA;
    } else {
            throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
    }
}

JDK8的無(wú)參構(gòu)造

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

JDK7的無(wú)參構(gòu)造

public ArrayList() {
    this(10);
}

JDK8中使用默認(rèn)的無(wú)參構(gòu)造方法初始化ArrayList時(shí),做了延遲優(yōu)化根资,在未執(zhí)行add() 方法前ArrayList 數(shù)組中的實(shí)際大小還是0,等到第一次添加元素的時(shí)候才進(jìn)行默認(rèn)長(zhǎng)度為10的數(shù)組初始化同窘。

傳入一個(gè)集合對(duì)象的構(gòu)造方法玄帕,構(gòu)造一個(gè)包含此集合元素的ArrayList

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
    }
}

ArrayList是如何實(shí)現(xiàn)動(dòng)態(tài)增長(zhǎng)的?

首先看add(E e)方法

public boolean add(E e) {
    // 判斷添加此元素時(shí)數(shù)組是否會(huì)超出想邦,超出則增長(zhǎng)數(shù)組
    ensureCapacityInternal(size + 1);
    // 添加元素
    elementData[size++] = e;
    return true;
}
/**
 * 此方法用于判斷當(dāng)添加這個(gè)元素時(shí)數(shù)組容量是否超出裤纹,超出則自動(dòng)增長(zhǎng)
 */
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 如果數(shù)組是通過(guò)默認(rèn)構(gòu)造方法實(shí)例化的,elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 將返回true
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 返回最大的值 丧没,如果minCapacity大于10則返回minCapacity的值
            return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    // fail-fast機(jī)制鹰椒,并發(fā)修改會(huì)拋出異常 throw new ConcurrentModificationException()
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
            // 新增元素后的數(shù)組長(zhǎng)度超過(guò)了當(dāng)前數(shù)組長(zhǎng)度,所以調(diào)用增加數(shù)組長(zhǎng)度的方法
            grow(minCapacity);
}

看一看grow(int minCapacity)方法就能知道ArrayList是如何自動(dòng)增長(zhǎng)容量的了

// 分配的最大數(shù)組大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // 增長(zhǎng)后的容量等于舊容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
     // MAX_ARRAY_SIZE為int的最大值減8呕童,如果增長(zhǎng)后的容量超過(guò)該值漆际,則直接返回int的最大值,否則返回該值
    if (newCapacity - MAX_ARRAY_SIZE > 0);
            newCapacity = hugeCapacity(minCapacity);
    // 使用的是Arrays.copyOf()方法將原數(shù)組中的元素拷貝到新增數(shù)組中夺饲,新增數(shù)組的長(zhǎng)度即是newCapacity
    elementData = Arrays.copyOf(elementData, newCapacity);
}

這里列出hugeCapacity(int minCapacity)方法

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}

JDK6中 int newCapacity = (oldCapacity * 3)/2 + 1; 增長(zhǎng)容量等于舊容量的1.5倍加1奸汇,JDK8中使用了位運(yùn)算施符,直接增長(zhǎng)為舊容量的1.5倍。

手動(dòng)調(diào)整ArrayList的容量

在添加大量元素前擂找,可以通過(guò)調(diào)用ensureCapacity(int minCapacity)方法來(lái)手動(dòng)增加ArrayList的容量戳吝,以減少遞增式再分配的數(shù)量

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
    }
}

trimToSize()方法可以將ArrayList的容量調(diào)整為實(shí)際元素的大小

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
            elementData = (size == 0)
                ? EMPTY_ELEMENTDATA
                : Arrays.copyOf(elementData, size);
    }
}

fail-fast機(jī)制

由于ArrayList不是線程安全的,所以如果在使用迭代器的過(guò)程中有其它線程修改了ArrayList贯涎,那么將會(huì)拋出 throw new ConcurrentModificationException()的異常听哭,這就是fail-fast機(jī)制(快速失敗)塘雳。
fail-fast機(jī)制是通過(guò)modCount字段來(lái)判斷的欢唾,modCount字段是父類AbstractList的字段,在每次修改ArrayList時(shí)粉捻,modCount字段都會(huì)自動(dòng)加1礁遣,迭代器初始化時(shí)會(huì)將modCount的值賦給迭代器的expectedModCount字段。
ArrayList的迭代器內(nèi)部實(shí)現(xiàn)(部分)

public Iterator<E> iterator() {
    return new Itr();
}
        
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    Itr() {}

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
                throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
                throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
                throw new IllegalStateException();
        checkForComodification();

        try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
        }
    }
}

在執(zhí)行next()肩刃、remove()方法時(shí)都會(huì)調(diào)用checkForComodification()方法祟霍,判斷expectedModCount是否還等于modCount,如果不相等則說(shuō)明已經(jīng)有其它線程修改了ArrayList盈包,這時(shí)就將拋出異常throw new ConcurrentModificationException()沸呐。

final void checkForComodification() {
    if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
}

待補(bǔ)充。呢燥。崭添。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叛氨,隨后出現(xiàn)的幾起案子呼渣,更是在濱河造成了極大的恐慌,老刑警劉巖寞埠,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屁置,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡仁连,警方通過(guò)查閱死者的電腦和手機(jī)蓝角,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饭冬,“玉大人使鹅,你說(shuō)我怎么就攤上這事〔伲” “怎么了患朱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扰魂。 經(jīng)常有香客問(wèn)我麦乞,道長(zhǎng)蕴茴,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任姐直,我火速辦了婚禮倦淀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘声畏。我一直安慰自己撞叽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布插龄。 她就那樣靜靜地躺著愿棋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪均牢。 梳的紋絲不亂的頭發(fā)上糠雨,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音徘跪,去河邊找鬼甘邀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛垮庐,可吹牛的內(nèi)容都是我干的松邪。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼哨查,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼逗抑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起寒亥,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤邮府,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后护盈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體挟纱,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年腐宋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片檀轨。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胸竞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出参萄,到底是詐尸還是另有隱情卫枝,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布讹挎,位于F島的核電站校赤,受9級(jí)特大地震影響吆玖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜马篮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一沾乘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浑测,春花似錦翅阵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至岖圈,卻和暖如春讹语,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜂科。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工顽决, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人崇摄。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓擎值,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親逐抑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸠儿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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