ArrayList源碼分析

List

List是一個維持內(nèi)部元素有序的采集器热鞍,其中的每個元素都會擁有一個索引,每個元素都可以
通過他的索引獲取到元素,并且索引的開始下標(biāo)是從0開始的申眼,List中的元素還可重復(fù)啦鸣。
而Set中不不含重復(fù)元素的潮饱。

以上便List的定義。實際中List僅是一個接口诫给,并沒有具體的方法實現(xiàn)香拉,只是定義好了統(tǒng)一的方法啦扬。

以下便是List的繼承結(jié)構(gòu):

  • Iterable
    • Collection
      • List

我們先來看看頂級的Iterable:

Instances of classes that implement this interface can be used with
the enhanced for loop.
也就是說繼承了這個接口能增強(qiáng)子類的循環(huán)能力。

Iterable中有唯一定義的接口方法:

Iterator<T> iterator();

他的作用就是返回一個Iterator<T>的實例凫碌。

接下來看看Iterator到底是什么東西

他是一個對象序列的迭代器扑毡,例如說集合。
它擁有三個接口方法:

//是否還有更多的沒有被迭代的元素盛险,有則返回true僚楞,否則返回false
public boolean hasNext();

 //返回下一個對象元素,并且是迭代器前進(jìn)枉层,如果沒有元素了的話會拋出NoSuchElementException
 public E next();

 //移除最后通過next()返回對象元素泉褐。此方法只能在調(diào)用next()后才能調(diào)用,否則會拋出IllegalStateException
 public void remove();

我們再來看看Collection接口:

Collection是所受Collection類型的根節(jié)點鸟蜡,也就是說所有的集合類型的都會實現(xiàn)這個接口膜赃。

方法 說明
add(E object) 嘗試將一個對象添加到Collection中,保證添加成功了該對象元素就包含在Collection中揉忘。在實現(xiàn)該接口方法的類中跳座,需要根據(jù)具體的添加規(guī)則拋出相應(yīng)的Exception。注意一點是拋出異常了就不會返回false,而添加成功會返回true泣矛。
addAll(Collection<? extends E> collection) 試圖將參數(shù)中的collection的所有元素添加到當(dāng)前實例中的Collection中疲眷。添加成功返回ture,否則返回false您朽。
clear() 將原本collection中的元素全部刪除狂丝。如果移除操作不允許會拋出UnsupportedOperationException。
contains(Object object) 遍歷Collection中的所有元素哗总,找到一個相等的元素則返回true几颜,否則返回false⊙肚可能拋出的異常:ClassCastException蛋哭,NullPointerException。
containsAll(Collection<?> collection) Collection是是否包含參數(shù)中collection中的每個元素涮母,即使是每個參數(shù)僅僅包含一次都會返回true谆趾,否則返回false。參數(shù)collection==null 或者 collection中至少包含一個null元素的時候回拋出NullPointerException
equals(Object object) 當(dāng)前Collection中與給定的object是否相等叛本。
hashCode() 返回Collection中所有元素的哈希值和沪蓬,主要用于比較。
isEmpty() 是否Collection中沒有元素炮赦。
Iterator<E> iterator() 返回一個迭代器實例怜跑,用來訪問Collection中的內(nèi)容。
remove(Object object) 將Collection中與參數(shù)object相等的元素⌒苑遥可能拋出的異常:UnsupportedOperationException,ClassCastException,NullPointerException
removeAll(Collection<?> collection) 將在Collection中包含參數(shù)collection中的元素移除峡眶。返回的結(jié)果是Collection中不包含有參數(shù)collection中的元素。
retainAll(Collection<?> collection) 將Collection中不包含在參數(shù)collection中的元素移除植锉。返回的結(jié)果是Collection中包含有參數(shù)collection中的元素辫樱。
size() 返回Collection中元素的個數(shù),如果個數(shù)大于Integer.MAX_VALUE俊庇,返回的結(jié)果是Integer.MAX_VALUE
Object[] toArray() 將Collection中的所有元素根據(jù)迭代順序以一個新數(shù)組返回狮暑,即使Collection的底層已經(jīng)是一個數(shù)組結(jié)構(gòu)。
<T> T[] toArray(T[] array) 將Collection中的元素根據(jù)迭代順序添加到給定的array中辉饱,如果array不能裝下Collection中的所有元素則會新建一個對應(yīng)類型搬男,對應(yīng)大小的數(shù)組,如果array的大小大于Collection的元素個數(shù)彭沼,則array多余的索引位置元素置為null缔逛。

而List在Collection的基礎(chǔ)上增加了以下接口方法:

方法 說明
add(int location, E object) 在索引location處插入一個元素,location==size()的話姓惑,直接在末尾添加褐奴。<size的話,在location處插入于毙,location之后的元素依次后移敦冬。location < 0 或者 location > size()則拋出IndexOutOfBoundsException
boolean addAll(int location, Collection<? extends E> collection) 在指定索引處插入一個contains的所有元素
E get(int location) 返回指定索引處的元素。location < 0 或者 location > size()則拋出IndexOutOfBoundsException
int indexOf(Object object) 返回一個指定object元素在list中的索引唯沮,沒有此元素則返回-1
int lastIndexOf(Object object) 最后一個等于指定object元素的索引脖旱,沒有則返回-1
remove(int location) 根據(jù)索引移除元素,location < 0 或者 location > size()則拋出IndexOutOfBoundsException
remove(Object object) 找到并移除了該元素則返回true烂翰,否則返回false
E set(int location, E object) 將指定索引位置設(shè)置為元素object夯缺。會有IndexOutOfBoundsException蚤氏,ClassCastException甘耿。
List<E> subList(int start, int end) 返回索引start到end處的子列表,會有IndexOutOfBoundsException竿滨。

此外還有:

ListIterator<E> listIterator();
ListIterator<E> listIterator(int location);

其中ListIterator繼承子Iterator佳恬,新添加了幾個方法:

public boolean hasPrevious();
public int nextIndex();
public E previous();
public int previousIndex();
void set(E object);

ArrayList

ArrayList繼承自AbstractList,實現(xiàn)了Cloneable于游,Serializable毁葱,RandomAccess接口
而AbstractList繼承自AbstractCollection<E>實現(xiàn)了List<E>接口,是一個抽象類贰剥,他的子類必須實現(xiàn)iterator(),size()這個兩個抽象方法倾剿,以使得可以創(chuàng)建處一個不可變的集合。而創(chuàng)建一個可修改的集合類型則需要重寫add()方法。

ArrayList是List的一個基于數(shù)組的實現(xiàn)類前痘,支持增加凛捏,移除,替換等元素操作芹缔。并且支持所有元素類型包括null坯癣。它的所有操作同步進(jìn)行的。而CopyOnWriteArrayList則可以用于高度并發(fā)最欠,頻繁遍歷的情形示罗。

數(shù)組容量增長步長。

private static final int MIN_CAPACITY_INCREMENT = 12;

構(gòu)造函數(shù)

初始化時指定數(shù)組容量芝硬,直到自己數(shù)據(jù)容量的時候蚜点,最好都指定。默認(rèn)是一個空數(shù)組拌阴。

public ArrayList(int capacity) {
    if (capacity < 0) {
        throw new IllegalArgumentException("capacity < 0: " + capacity);
    }
    array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}

public ArrayList() {
    array = EmptyArray.OBJECT;
  }

 //指定一個collection初始化的時候
 public ArrayList(Collection<? extends E> collection) {
   if (collection == null) {
       throw new NullPointerException("collection == null");
   }
   //先轉(zhuǎn)化成數(shù)組
   Object[] a = collection.toArray();

   if (a.getClass() != Object[].class) {
       //創(chuàng)建一個長度為collection長度的新數(shù)組禽额,并將collection數(shù)組復(fù)制到新數(shù)組中
       Object[] newArray = new Object[a.length];
       System.arraycopy(a, 0, newArray, 0, a.length);
       a = newArray;
   }
   array = a;
   size = a.length;
}

添加

添加的時候基本策略是,有指定添加的索引位置的時候就檢查是否索引越界皮官,如果是則直接拋出越界異常脯倒。然后是檢查當(dāng)前數(shù)組是否已經(jīng)裝滿,如果是則計算新的數(shù)組容量捺氢,并創(chuàng)建一個新的數(shù)組藻丢,原數(shù)組的元素復(fù)制到新數(shù)組并將新添加的元素加入到list的末尾,更新size值摄乒。

@Override public boolean add(E object) {
    Object[] a = array;
    int s = size;
    //原數(shù)組已滿
    if (s == a.length) {
        Object[] newArray = new Object[s +
                (s < (MIN_CAPACITY_INCREMENT / 2) ?
                 MIN_CAPACITY_INCREMENT : s >> 1)];
        System.arraycopy(a, 0, newArray, 0, s);
        array = a = newArray;
    }
    a[s] = object;
    size = s + 1;//容量加一
    modCount++;
    return true;
}

該方法是根據(jù)傳入的當(dāng)前數(shù)組容量值計算并返回新的容量值悠反,時空權(quán)衡。
擴(kuò)容策略:

  1. 當(dāng)前容量小于最下增長量的一半:
    當(dāng)前容量+最小增長量
  2. 當(dāng)前容量大于等于最下增長量的一半:
    當(dāng)前容量+當(dāng)前容量/2
private static int newCapacity(int currentCapacity) {
    int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
            MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
    return currentCapacity + increment;
}

查找

方法 默認(rèn)返回值
public boolean contains(Object object) false
public int indexOf(Object object) -1
public int lastIndexOf(Object object) -1
public boolean remove(Object object) false

當(dāng)需要查找的元素對象不為空的時候馍佑,從頭開始遍歷數(shù)組的元素斋否,equals則直接返回對應(yīng)類型。
查找的元素對象為空時拭荤,從頭開始遍歷數(shù)組的元素茵臭,遇到一個 ==null的元素的時候則直接返回對應(yīng)類型。

Object[] a = array;
int s = size;
if (object != null) {
    for (int i = 0; i < s; i++) {
        if (object.equals(a[i])) {
            ...
            return ...;
        }
    }
} else {
    for (int i = 0; i < s; i++) {
        if (a[i] == null) {
            ...
            return ...;
        }
    }
}

remove一個元素的時候會有這么一個操作:a[s] = null; 是為了防止內(nèi)存溢出舅世。

指定位置元素移除: public E remove(int index)旦委,只需檢測時候下標(biāo)越界。不越界則移除該位置的元素雏亚。

移除元素位置之后的元素都前移一位
System.arraycopy(a, index + 1, a, index, --s - index);
//此時a[s] == a[s-1]缨硝,所以可以刪除末尾的那個a[s]
a[s] = null;  // Prevent memory leak
size = s;

在每次list的增刪改操作的時候都會modCount++。modCount是用來記錄list的修改次數(shù)罢低,
主要是在ArrayList總的內(nèi)部迭代器ArrayListIterator中使用

ArrayListIterator

//剩余沒有被迭代到的元素數(shù)量
private int remaining = size;
//可被使用remove()移除的元素的索引, -1表示沒有可以被移除的元素
private int removalIndex = -1;
//期待的list操作次數(shù)查辩,可判斷是否存在并發(fā)操作
private int expectedModCount = modCount;

//是否迭代完
public boolean hasNext() {
    return remaining != 0;
}

public E next() {
    ArrayList<E> ourList = ArrayList.this;
    int rem = remaining;
    //存在并發(fā)操作
    if (ourList.modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    //已經(jīng)迭代完,繼續(xù)迭代拋出異常, 用hasNext()做檢查宜岛,避免此異常
    if (rem == 0) {
        throw new NoSuchElementException();
    }
    remaining = rem - 1;
    //ourList.size - rem處的元素
    return (E) ourList.array[removalIndex = ourList.size - rem];
}

public void remove() {
    Object[] a = array;
    int removalIdx = removalIndex;

    ...

    System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
    a[--size] = null;  // Prevent memory leak
    removalIndex = -1;//重新置為-1匀钧,表示該索引的元素已移除。除非被next()更新谬返。
    expectedModCount = ++modCount;//會更新list的操作次數(shù)
}

再來看看的ArrayList的迭代器中定義的equals策略

public boolean equals(Object o) {
    //引用相等
    if (o == this) {
        return true;
    }
    //參數(shù)o不是List的子類
    if (!(o instanceof List)) {
        return false;
    }
    List<?> that = (List<?>) o;
    int s = size;
    //兩個list的size不想等
    if (that.size() != s) {
        return false;
    }
    Object[] a = array;

    if (that instanceof RandomAccess) {
        for (int i = 0; i < s; i++) {
            Object eThis = a[i];
            Object ethat = that.get(i);
            if (eThis == null ? ethat != null : !eThis.equals(ethat)) {
                return false;
            }
        }
    } else {  // 參數(shù)的list不支持隨機(jī)訪問之斯,則使用迭代器進(jìn)行迭代
        Iterator<?> it = that.iterator();
        for (int i = 0; i < s; i++) {
            Object eThis = a[i];
            Object eThat = it.next();
            if (eThis == null ? eThat != null : !eThis.equals(eThat)) {
                return false;
            }
        }
    }
    return true;
}

至此,ArrayList已被你KO遣铝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末佑刷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子酿炸,更是在濱河造成了極大的恐慌瘫絮,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件填硕,死亡現(xiàn)場離奇詭異麦萤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扁眯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門壮莹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人姻檀,你說我怎么就攤上這事命满。” “怎么了绣版?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵胶台,是天一觀的道長。 經(jīng)常有香客問我杂抽,道長诈唬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任缩麸,我火速辦了婚禮铸磅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匙睹。我一直安慰自己愚屁,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布痕檬。 她就那樣靜靜地躺著,像睡著了一般送浊。 火紅的嫁衣襯著肌膚如雪梦谜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機(jī)與錄音唁桩,去河邊找鬼闭树。 笑死,一個胖子當(dāng)著我的面吹牛荒澡,可吹牛的內(nèi)容都是我干的报辱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼单山,長吁一口氣:“原來是場噩夢啊……” “哼碍现!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起米奸,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤昼接,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后悴晰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慢睡,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年铡溪,在試婚紗的時候發(fā)現(xiàn)自己被綠了漂辐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡棕硫,死狀恐怖者吁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饲帅,我是刑警寧澤复凳,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站灶泵,受9級特大地震影響育八,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赦邻,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一髓棋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惶洲,春花似錦按声、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铐料,卻和暖如春渐裂,著一層夾襖步出監(jiān)牢的瞬間豺旬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工柒凉, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留族阅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓膝捞,卻偏偏與公主長得像坦刀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蔬咬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

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