ArrayList源碼分析

一 成員變量解析

/**
 * 默認(rèn)初始化容量.
*/
private static final int DEFAULT_CAPACITY = 10;


/**
 * ArrayList存儲(chǔ)數(shù)據(jù)的數(shù)組.
 * ArrayList的容量就是該數(shù)組的長(zhǎng)度
 * 如果elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * 那么當(dāng)?shù)匾粋€(gè)元素加進(jìn)來時(shí),elementData 的長(zhǎng)度會(huì)擴(kuò)充為DEFAULT_CAPACITY.
*/
transient Object[] elementData;

/**
 * 數(shù)組的容量
*/
private int size;

/**
 * 數(shù)組可申請(qǐng)的最大長(zhǎng)度
 * 有些虛擬機(jī)需要在數(shù)組中保存頭部信息
 * 申請(qǐng)長(zhǎng)度超出范圍會(huì)引起
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

二 關(guān)鍵方法解析

1 添加元素

public boolean add(E e) {
    // 增加ArrayList的容量(擴(kuò)容敢靡,后邊詳述)
    ensureCapacityInternal(size + 1); 
    elementData[size++] = e;
    return true;
}

2 擴(kuò)容

ArrayList默認(rèn)大小是10桃移,如果底層數(shù)組大小不夠了怎么辦见转,答案是進(jìn)行擴(kuò)容誊稚,這就是為何說ArrayList的底層是基于動(dòng)態(tài)數(shù)組實(shí)現(xiàn)的原因管削。

private void ensureCapacityInternal(int minCapacity) {
    /*
     * 如果elementData還是默認(rèn)的空數(shù)組倒脓,
     * 那么會(huì)設(shè)置elementData的最小容量,
     * 最小容量為DEFAULT_CAPACITY和minCapacity的最大值
    */
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

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

    // 如果最小容量大于elementData長(zhǎng)度則擴(kuò)容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // 設(shè)置newCapacity 為oldCapacity 的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果newCapacity小于minCapacity 含思,則設(shè)newCapacity=minCapacity 
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果newCapacity超出數(shù)組可申請(qǐng)的最大長(zhǎng)度則重新設(shè)置newCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

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

可以看到在每次擴(kuò)容時(shí)新的容量大小是舊容量大小的1.5倍崎弃,最后會(huì)調(diào)用Arrays的copyOf方法將舊數(shù)組的內(nèi)容復(fù)制到新數(shù)組中。

3 獲取元素

@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

/**
 * 檢查index是否越界含潘,該方法并不檢查index是否為負(fù)數(shù)
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

4 替換特定位置的元素

public E set(int index, E element) {
    rangeCheck(index);

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

5 插入元素

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

看到在插入元素的時(shí)候饲做,System.arraycopy方法將array中從index起始的子集復(fù)制到從index+1開始的地方,然后再index出加入新插入的元素遏弱。

6 刪除元素

刪除元素有兩種方式:

  1. 根據(jù)index刪除和根據(jù)
public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = 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;
}
  1. 根據(jù)元素o刪除盆均,這會(huì)刪除arrayList中第一個(gè)和o匹配的元素
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;
}

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

從上面的幾個(gè)過程總結(jié)一下ArrayList的優(yōu)缺點(diǎn)。ArrayList的優(yōu)點(diǎn)如下:

  1. ArrayList底層以數(shù)組實(shí)現(xiàn)漱逸,是一種隨機(jī)訪問模式泪姨,再加上它實(shí)現(xiàn)了RandomAccess接口,因此查找也就是get的時(shí)候非呈问悖快
  2. ArrayList在順序添加一個(gè)元素的時(shí)候非常方便肮砾,只是往數(shù)組里面添加了一個(gè)元素而已

不過ArrayList的缺點(diǎn)也十分明顯:

  1. 刪除元素的時(shí)候,涉及到一次元素復(fù)制袋坑,如果要復(fù)制的元素很多仗处,那么就會(huì)比較耗費(fèi)性能
  2. 插入元素的時(shí)候,涉及到一次元素復(fù)制咒彤,如果要復(fù)制的元素很多疆柔,那么就會(huì)比較耗費(fèi)性能
    因此,ArrayList比較適合順序添加镶柱、隨機(jī)訪問的場(chǎng)景旷档。

四 ArrayList和Vector的區(qū)別

  1. Vector是線程安全的,ArrayList是線程非安全的
  2. Vector可以指定增長(zhǎng)因子歇拆,如果該增長(zhǎng)因子指定了鞋屈,那么擴(kuò)容的時(shí)候會(huì)每次新的數(shù)組大小會(huì)在原數(shù)組的大小基礎(chǔ)上加上增長(zhǎng)因子;如果不指定增長(zhǎng)因子故觅,那么就給原數(shù)組大小*2厂庇,源代碼是這樣的:
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);

五 為什么ArrayList的elementData是用transient修飾的?

不知道大家有沒有想過输吏,為什么elementData是使用transient修飾的呢权旷?關(guān)于這個(gè)問題,說說我的看法贯溅。我們看一下ArrayList的定義:

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

看到ArrayList實(shí)現(xiàn)了Serializable接口拄氯,這意味著ArrayList是可以被序列化的躲查,用transient修飾elementData意味著我不希望elementData數(shù)組被序列化。這是為什么译柏?因?yàn)樾蛄谢疉rrayList的時(shí)候镣煮,ArrayList里面的elementData未必是滿的,比方說elementData有10的大小鄙麦,但是我只用了其中的3個(gè)典唇,那么是否有必要序列化整個(gè)elementData呢?顯然沒有這個(gè)必要胯府,因此ArrayList中重寫了writeObject方法:

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

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

每次序列化的時(shí)候調(diào)用這個(gè)方法介衔,先調(diào)用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它盟劫,然后遍歷elementData夜牡,只序列化那些有的元素,這樣:

  1. 加快了序列化的速度
  2. 減小了序列化之后的文件大小
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侣签,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子急迂,更是在濱河造成了極大的恐慌影所,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僚碎,死亡現(xiàn)場(chǎng)離奇詭異猴娩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)勺阐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門卷中,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渊抽,你說我怎么就攤上這事蟆豫。” “怎么了懒闷?”我有些...
    開封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵十减,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我愤估,道長(zhǎng)帮辟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任玩焰,我火速辦了婚禮由驹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昔园。我一直安慰自己蔓榄,他們只是感情好闹炉,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著润樱,像睡著了一般渣触。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壹若,一...
    開封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天嗅钻,我揣著相機(jī)與錄音,去河邊找鬼店展。 笑死养篓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赂蕴。 我是一名探鬼主播柳弄,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼概说!你這毒婦竟也來了碧注?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤糖赔,失蹤者是張志新(化名)和其女友劉穎萍丐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體放典,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逝变,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奋构。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壳影。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖弥臼,靈堂內(nèi)的尸體忽然破棺而出宴咧,到底是詐尸還是另有隱情,我是刑警寧澤醋火,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布悠汽,位于F島的核電站,受9級(jí)特大地震影響芥驳,放射性物質(zhì)發(fā)生泄漏柿冲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一兆旬、第九天 我趴在偏房一處隱蔽的房頂上張望假抄。 院中可真熱鬧,春花似錦、人聲如沸宿饱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谬以。三九已至强饮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間为黎,已是汗流浹背邮丰。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铭乾,地道東北人剪廉。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像炕檩,于是被迫代替她去往敵國(guó)和親斗蒋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

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