讀源碼之ArrayList

簡(jiǎn)單剖析下常用的ArrayList類的源碼

ArrayList的核心還是基于數(shù)組實(shí)現(xiàn)的。

//實(shí)現(xiàn)了Serializable接口,因此它支持序列化,能夠通過(guò)序列化傳輸
//實(shí)現(xiàn)了RandomAccess接口垫毙,支持快速隨機(jī)訪問(wèn)贪嫂,實(shí)際上就是通過(guò)下標(biāo)序號(hào)進(jìn)行快速訪問(wèn)
//實(shí)現(xiàn)了Cloneable接口,能被克隆俐巴。
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    //提供序列化用的
    private static final long serialVersionUID = 8683452581122892189L;
    
    //默認(rèn)的初始容量為10
    private static final int DEFAULT_CAPACITY = 10;
    
    //...
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
    //arraylist用來(lái)保存對(duì)象的數(shù)組,transient告訴序列化的時(shí)候不要管這個(gè)數(shù)組
    transient Object[] elementData;
    
    //arraylist的大小
    private int size;
    
    //構(gòu)造函數(shù)硬爆,不能傳入負(fù)數(shù)欣舵,否則報(bào)錯(cuò),然后初始化elementData數(shù)組的大小
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
    
    /*
  空構(gòu)造函數(shù)缀磕,以前的代碼直接初始化容量為10的容量數(shù)組:this(10)
  現(xiàn)在Android Api25中缘圈,這里直接初始化一個(gè)空數(shù)組,等add的時(shí)候再設(shè)置容量為10
  懶加載模式
   */
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }
    
    //構(gòu)造函數(shù)袜蚕,將c集合里面的東西放array里面
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
    
    /*
  將array的size設(shè)為實(shí)際size大小
  modCount表示修改的次數(shù)糟把,給迭代器iterator用的,實(shí)現(xiàn)fail-fast機(jī)制
  用于多線程的時(shí)候modCount不一致,快速拋出異常
   */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = Arrays.copyOf(elementData, size);
        }
    }
    
    //這個(gè)方法就是確保array的容量至少為minCapacity
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != EMPTY_ELEMENTDATA)
            // any size if real element table
            ? 0
            // larger than default for empty table. It's already supposed to be
            // at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }
    
    //接上面的方法牲剃,增加修改次數(shù)遣疯,判斷需要的最小容量大于實(shí)際容量再操作
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    /*
  以前增加的容量為old的三分之二再加一:
  int newCapacity = (oldCapacity * 3)/2 + 1
  現(xiàn)在增加的容量為old的二分之一:
  int newCapacity = oldCapacity + (oldCapacity >> 1)
   */
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        //右移一位表示除以2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //這個(gè)newCapacity < minCapacity的代碼下面這樣寫有什么優(yōu)勢(shì)?
        //下面主要是判斷oldCapacity=1的情況下凿傅,newCapacity其實(shí)等于oldCapacity的情況缠犀?
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    
    public int size() {return size;}
    
    public boolean isEmpty() {return size == 0;}
    
    //判斷包含
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    
    //這里先判斷null對(duì)象,非null的對(duì)象是通過(guò)equals()來(lái)判斷的
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    
    //反過(guò)來(lái)查詢返回第一個(gè)的i
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    
    //淺拷貝数苫,并且將修改次數(shù)置0
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
    
    //轉(zhuǎn)化為數(shù)組
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
    
    /*
  返回ArrayList元素組成的數(shù)組
  如果數(shù)組a的容量小于list,則新建一個(gè)數(shù)組辨液,反之直接復(fù)制到數(shù)組
   */
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
    
    //取出列表中指定的元素
    public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        return (E) elementData[index];
    }
    
    //替換指定位置的元素
    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;
    }
    
    //添加元素
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        //和ensureExplicitCapacity的區(qū)別在這里
        //如果是一開始沒有指定大小的初始化list虐急,則比較默認(rèn)的容量和傳入的值哪個(gè)大,就用哪個(gè)
        //反正不要小于10即DEFAULT_CAPACITY
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    
    //添加元素到指定位置滔迈,指定位置和之后的元素后移一個(gè)位置
    public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
    
    //刪除指定位置的元素止吁,就是將指定位置后面的元素都往前挪一個(gè)位置,再把最后一個(gè)元素置空亡鼠,交給垃圾回收器處理
    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;
    }
    
    //移除指定元素對(duì)象
    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;
    }
    
    //上面的內(nèi)部方法赏殃,原理主要還是找出那個(gè)對(duì)象的位置,然后跟remove(index)類似
    private void fastRemove(int index) {
        modCount++;
        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
    }
    
    //將所有元素置空间涵,交給gc處理
    public void clear() {
        modCount++;

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

        size = 0;
    }
    
    //將c集合轉(zhuǎn)化為數(shù)組仁热,然后拷貝到list數(shù)組后面
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    
    //上面一個(gè)方法指定位置的拷貝
    public boolean addAll(int index, Collection<? extends E> c) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
    
    //保護(hù)方法,不是供程序員調(diào)用的勾哩,核心還是拷貝方法抗蠢,將移除的那些置空
    protected void removeRange(int fromIndex, int toIndex) {
        // Android-changed : Throw an IOOBE if toIndex < fromIndex as documented.
        // All the other cases (negative indices, or indices greater than the size
        // will be thrown by System#arrayCopy.
        if (toIndex < fromIndex) {
            throw new IndexOutOfBoundsException("toIndex < fromIndex");
        }

        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }
    
    //序列化的寫入,讀取思劳,迭代器部分
    ...
    
}

其他注意的就是ArrayList基于數(shù)組實(shí)現(xiàn)迅矛,可以通過(guò)下標(biāo)索引直接查找到指定位置的元素,因此查找效率高潜叛,但每次插入或刪除元素秽褒,就要大量地移動(dòng)元素,插入刪除元素的效率低威兜;在查找給定元素索引值等的方法中销斟,源碼都將該元素的值分為null和不為null兩種情況處理,ArrayList中允許元素為null椒舵。

自己先看一遍源碼再看別人的分析效果棒棒的,參考:
ArrayList源碼剖析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚂踊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子笔宿,更是在濱河造成了極大的恐慌犁钟,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泼橘,死亡現(xiàn)場(chǎng)離奇詭異涝动,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)炬灭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門捧存,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事昔穴。” “怎么了提前?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵吗货,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我狈网,道長(zhǎng)宙搬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任拓哺,我火速辦了婚禮勇垛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘士鸥。我一直安慰自己闲孤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布烤礁。 她就那樣靜靜地躺著讼积,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脚仔。 梳的紋絲不亂的頭發(fā)上勤众,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音鲤脏,去河邊找鬼们颜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛猎醇,可吹牛的內(nèi)容都是我干的窥突。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼姑食,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼波岛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起音半,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤则拷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后曹鸠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煌茬,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年彻桃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坛善。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖眠屎,靈堂內(nèi)的尸體忽然破棺而出剔交,到底是詐尸還是另有隱情,我是刑警寧澤改衩,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布岖常,位于F島的核電站,受9級(jí)特大地震影響葫督,放射性物質(zhì)發(fā)生泄漏竭鞍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一橄镜、第九天 我趴在偏房一處隱蔽的房頂上張望偎快。 院中可真熱鬧,春花似錦洽胶、人聲如沸晒夹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惋戏。三九已至,卻和暖如春他膳,著一層夾襖步出監(jiān)牢的瞬間响逢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工棕孙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舔亭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓蟀俊,卻偏偏與公主長(zhǎng)得像钦铺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肢预,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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

  • 一.線性表 定義:零個(gè)或者多個(gè)元素的有限序列矛洞。也就是說(shuō)它得滿足以下幾個(gè)條件:??①該序列的數(shù)據(jù)元素是有限的。??②...
    Geeks_Liu閱讀 2,688評(píng)論 1 12
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法烫映,類相關(guān)的語(yǔ)法沼本,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法锭沟,異常的語(yǔ)法抽兆,線程的語(yǔ)...
    子非魚_t_閱讀 31,582評(píng)論 18 399
  • 一辫红、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對(duì)于byte類型而言...
    龍貓小爺閱讀 4,254評(píng)論 0 16
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,515評(píng)論 25 707
  • 1凭涂、英語(yǔ)跟讀,走遍美國(guó)贴妻,60分鐘切油,bassinet。Steward家庭整體氣氛和諧的讓人心生嫉妒名惩,哈哈白翻。TLC,t...
    長(zhǎng)海1994閱讀 160評(píng)論 2 0