ArrayList源碼分析

List

List是一個維持內部元素有序的采集器,其中的每個元素都會擁有一個索引,每個元素都可以
通過他的索引獲取到元素,并且索引的開始下標是從0開始的,List中的元素還可重復。
而Set中不不含重復元素的。

以上便List的定義。實際中List僅是一個接口,并沒有具體的方法實現,只是定義好了統一的方法。

以下便是List的繼承結構:

  • Iterable
    • Collection
      • List

我們先來看看頂級的Iterable:

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

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

Iterator<T> iterator();

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

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

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

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

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

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

我們再來看看Collection接口:

Collection是所受Collection類型的根節點,也就是說所有的集合類型的都會實現這個接口。

方法 說明
add(E object) 嘗試將一個對象添加到Collection中,保證添加成功了該對象元素就包含在Collection中。在實現該接口方法的類中,需要根據具體的添加規則拋出相應的Exception。注意一點是拋出異常了就不會返回false,而添加成功會返回true。
addAll(Collection<? extends E> collection) 試圖將參數中的collection的所有元素添加到當前實例中的Collection中。添加成功返回ture,否則返回false。
clear() 將原本collection中的元素全部刪除。如果移除操作不允許會拋出UnsupportedOperationException。
contains(Object object) 遍歷Collection中的所有元素,找到一個相等的元素則返回true,否則返回false??赡軖伋龅漠惓#篊lassCastException,NullPointerException。
containsAll(Collection<?> collection) Collection是是否包含參數中collection中的每個元素,即使是每個參數僅僅包含一次都會返回true,否則返回false。參數collection==null 或者 collection中至少包含一個null元素的時候回拋出NullPointerException
equals(Object object) 當前Collection中與給定的object是否相等。
hashCode() 返回Collection中所有元素的哈希值和,主要用于比較。
isEmpty() 是否Collection中沒有元素。
Iterator<E> iterator() 返回一個迭代器實例,用來訪問Collection中的內容。
remove(Object object) 將Collection中與參數object相等的元素??赡軖伋龅漠惓#篣nsupportedOperationException,ClassCastException,NullPointerException
removeAll(Collection<?> collection) 將在Collection中包含參數collection中的元素移除。返回的結果是Collection中不包含有參數collection中的元素。
retainAll(Collection<?> collection) 將Collection中不包含在參數collection中的元素移除。返回的結果是Collection中包含有參數collection中的元素。
size() 返回Collection中元素的個數,如果個數大于Integer.MAX_VALUE,返回的結果是Integer.MAX_VALUE
Object[] toArray() 將Collection中的所有元素根據迭代順序以一個新數組返回,即使Collection的底層已經是一個數組結構。
<T> T[] toArray(T[] array) 將Collection中的元素根據迭代順序添加到給定的array中,如果array不能裝下Collection中的所有元素則會新建一個對應類型,對應大小的數組,如果array的大小大于Collection的元素個數,則array多余的索引位置元素置為null。

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

方法 說明
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) 根據索引移除元素,location < 0 或者 location > size()則拋出IndexOutOfBoundsException
remove(Object object) 找到并移除了該元素則返回true,否則返回false
E set(int location, E object) 將指定索引位置設置為元素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,實現了Cloneable,Serializable,RandomAccess接口
而AbstractList繼承自AbstractCollection<E>實現了List<E>接口,是一個抽象類,他的子類必須實現iterator(),size()這個兩個抽象方法,以使得可以創建處一個不可變的集合。而創建一個可修改的集合類型則需要重寫add()方法。

ArrayList是List的一個基于數組的實現類,支持增加,移除,替換等元素操作。并且支持所有元素類型包括null。它的所有操作同步進行的。而CopyOnWriteArrayList則可以用于高度并發,頻繁遍歷的情形。

數組容量增長步長。

private static final int MIN_CAPACITY_INCREMENT = 12;

構造函數

初始化時指定數組容量,直到自己數據容量的時候,最好都指定。默認是一個空數組。

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");
   }
   //先轉化成數組
   Object[] a = collection.toArray();

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

添加

添加的時候基本策略是,有指定添加的索引位置的時候就檢查是否索引越界,如果是則直接拋出越界異常。然后是檢查當前數組是否已經裝滿,如果是則計算新的數組容量,并創建一個新的數組,原數組的元素復制到新數組并將新添加的元素加入到list的末尾,更新size值。

@Override public boolean add(E object) {
    Object[] a = array;
    int s = size;
    //原數組已滿
    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;
}

該方法是根據傳入的當前數組容量值計算并返回新的容量值,時空權衡。
擴容策略:

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

查找

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

當需要查找的元素對象不為空的時候,從頭開始遍歷數組的元素,equals則直接返回對應類型。
查找的元素對象為空時,從頭開始遍歷數組的元素,遇到一個 ==null的元素的時候則直接返回對應類型。

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; 是為了防止內存溢出。

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

移除元素位置之后的元素都前移一位
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的修改次數,
主要是在ArrayList總的內部迭代器ArrayListIterator中使用

ArrayListIterator

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

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

public E next() {
    ArrayList<E> ourList = ArrayList.this;
    int rem = remaining;
    //存在并發操作
    if (ourList.modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    //已經迭代完,繼續迭代拋出異常, 用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的操作次數
}

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

public boolean equals(Object o) {
    //引用相等
    if (o == this) {
        return true;
    }
    //參數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 {  // 參數的list不支持隨機訪問,則使用迭代器進行迭代
        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。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容