1. 程式人生 > >ArrayList原始碼剖析~

ArrayList原始碼剖析~

包路徑:package java.util;

一、基本屬性

1、private  static  final  long  serialVersionUID  =  8683452581122892189L;   

該屬性是序列化的時候使用的,serialVersionUID是用來表明類的不同版本之間的相容性,若一個序列化類沒有宣告這樣一個static final的常量,JVM會根據各種引數為該類計算一個,對於同一個類,不同的JDK版本可能得出不同的serialVersionUID。

 2、private  static  final  int  DEFAULT_CAPACITY  =  10;        陣列的預設初始容量大小

 3、private  static  final  Object[ ]   EMPTY_ELEMENTDATA  =  { };       陣列的初始化使用

 4、private  transient  Object[ ]  elementData;         ArrayList的底層資料結構是陣列

transient關鍵字:在採用Java預設的序列化機制的時候,被該關鍵字修飾的屬性不會被序列化。

 5、private  int  size;      集合中儲存元素的個數

6、protected  transient  int  modCount  =  0;         

在父類AbstractList中定義了該屬性:modCount,是用來記錄AbstractList結構性變化的次數。在ArrayList所有涉及結構性變化的方法中(增加、刪除、修改等)都涉及modCount值++操作。

二、建構函式

1、有參構造如下:傳入的引數是陣列預設的初始化大小,直接對陣列elementData屬性進行初始化

public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

2、無參構造如下:elementData陣列為空

public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

3、有參構造如下:傳入的引數是一個集合型別的物件

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);
    }

三、繼承關係

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

繼承AbstractList類,實現了List<E>等的介面,ArrayList是List介面的一個實現類。

RandomAccess:該介面是一個標誌介面,檢視該介面的原始碼會發現,該介面是一個空的,但是隻要List集合實現這個介面,支援快速隨機訪問,我們即可以通過元素的序號快速獲取元素物件。總的來說,實現了這個介面的List,使用for迴圈獲取資料優於使用迭代器獲取資料,LinkedList沒有實現這個介面,那麼對於它來說,使用迭代器獲取資料優於使用for迴圈獲取資料。

Cloneable:可以進行克隆,

java.io.Serializable:可以進行序列化和反序列化的操作

四、增長方式

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }

預設陣列的初始化大小是10,按照原陣列大小的1.5倍方式進行擴容增長。

兩個if語句相當於是給擴容後的大小定了一個上下界。

五、CRUD

1、增加元素的方法(add、addAll)

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

傳入的引數是一個:元素

ensureCapacityInternal()方法:首先判斷陣列是否為空,若為空,陣列的容量大小設定為10,確保陣列的容量足夠存放新加入的元素,若不夠,需要進行擴容

將元素插入到elementData陣列中,相應的size+1

 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++;
    }

傳入的引數是兩個:元素和想要新增的位置下標

rangeCheckForAdd():該方法是用來檢查傳入的位置下標是否合法,不合法丟擲下標越界的異常

System.arraycopy():

public static native void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)

將陣列src從下標srcPos進行拷貝,一直拷貝length個元素到dest陣列中,在dest陣列中從destPos下標開始加入先前的srcPos陣列。

也就是說此處呼叫這個方法是要把插入的位置空出來,儲存插入的元素

addAll()方法同理上述兩個傳入引數不同的add()方法,只是傳入的引數由資料變成了集合型別的物件。

特點:可以儲存重複的資料,也可以儲存null

2、刪除元素的方法(remove、removeAll、removeRange)

 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;
    }

按下標刪除,首先檢查刪除下標的合法性,然後找出要刪除下標所對應的元素,使用system.arraycopy()方法進行值的挪動拷貝(覆蓋掉要刪除的下標元素),刪除元素意味著size要相應的減少。

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;
    }

按元素進行刪除,查詢元素,找到要刪除元素的下標,呼叫fastRemove()方法,傳入刪除下標,進行快速刪除。

protected void removeRange(int fromIndex, int toIndex) {
        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;
    }

從一個起始位置到結束位置之間的元素全部刪除,具體就不再解釋,該方法未呼叫別的陌生的方法,

removeAll():該方法同上述的思想,只是傳入的引數是一個集合型別的物件,呼叫了batchRemove方法進行刪除。

特點:陣列的起始元素下標是0,找到第一個出現的元素直接刪除並返回,時間複雜度:O(n)

3、獲取當前元素的方法(get)

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

        return elementData(index);
    }

通過下標進行獲取,首先檢查下標的合法性,通過所以位置直接查詢陣列elementData相對應的元素,時間複雜度:O(1),查詢效率很高。

4、遍歷ArrayList中的物件

 public Iterator<E> iterator() {
        return new Itr();//返回一個內部類物件
    }

iterator()方法是在AbatractList類當中實現了的,該方法返回AbstractList的一個內部類Itr物件。

六、總結

1、ArrayList基於陣列的方式實現,沒有容量的限制(擴容)

2、新增元素可能需要擴容(最好首先預判一下)

3、執行緒不安全

4、內部類:ArrayList有三個內部類

private  class  Itr  implements  Iterator<E>

 private  class  ListItr  extends  Itr  implements  ListIterator<E> 

 private  class  SubList  extends  AbstractList<E>  implements  RandomAccess

Itr實現了Iterator介面,重寫了裡面的hasNext()、next()、remove()方法。

ListItr繼承了Itr而且實現了ListIterator介面,重寫了hasPrevious(),nextIndex(),previousIndex(),previous(),set(E e),add(E e)等方法。

SubList繼承了AbstractList,實現了RandomAccess介面,類內部實現了對子序列的增刪改查等方法,但它同時也充分利用了內部類的優點,就是共享ArrayList的全域性變數,例如檢查器變數modCount,陣列elementData等,所以SubList進行的增刪改查操作都是對ArrayList的陣列進行的,並沒有建立新的陣列。