《JAVA1.8源碼分析》:ArrayList

《JAVA1.8源碼分析》:ArrayList

繼承體系

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

ArrayList實現了AbstractList接口,並繼承了list接口 ,同時實現了RandomAccess,Cloneable,Serializable接口,使其具有隨機訪問,複製和序列化的功能。

屬性

//初始容量大小爲10
1. private static final int DEFAULT_CAPACITY = 10;
//共享空常量數組
2. private static final Object[] EMPTY_ELEMENTDATA = {};
//共享空常量數組(與EMPTY_ELEMENTDATA的區別在於當第一個元素被加入進來的時候它知道如何擴張)
3. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存儲ArrayList中的元素(從這個可以看出,ArrayList是底層是借組於數組來實現的。)
4. transient Object[] elementData;
//存儲的元素個數
5. private int size;

構造函數
ArrayList具有三個構造函數

//1.無參數構造函數(elementData數據直接複製爲共享空常量數組)
public ArrayList() {
       this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
   }

構造一個初始容量爲 10 的空列表。

即當我們不提供參數而new一個對象時,底層的數組就是直接用長度爲10的空常量數組DEFAULTCAPACITY_EMPTY_ELEMENTDATA進行實例化。

//2.指定容量作爲參數的構造函數
public ArrayList(int initialCapacity) {
       if (initialCapacity > 0) {
           this.elementData = new Object[initialCapacity];
       } else if (initialCapacity == 0) {
           this.elementData = EMPTY_ELEMENTDATA;
       } else {
           throw new IllegalArgumentException("Illegal Capacity: "+
                                              initialCapacity);
       }
   }

從源碼可以看到,就是根據參數的大小作爲容量來實例化底層的數組對象,其中對參數的3中情況進行了處理。當參數小於0時,拋異常。當參數等於0時,用空的常量數組對象EMPTY_ELEMENTDATA來初始化底層數組elementData。

//3.Collection作爲參數的構造函數
//3.1 將集合轉換爲數組(c.toArray())並賦值給elementData
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
            //調用複製函數複製數據
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

從源碼可以看到,將容器Collection轉化爲數組賦給數組elementData,還對Collection轉化是否轉化爲了Object[]進行了檢查判斷。如果Collection爲空,則就將空的常量數組對象EMPTY_ELEMENTDATA賦給了elementData;

add函數

//函數功能:將制定的元素加入到List的末尾。
public boolean add(E e) {
       //添加修改次數以及判斷是否需要擴張數組長度,
       ensureCapacityInternal(size + 1);  // Increments modCount!!
       //插入數據,數據量+1
       elementData[size++] = e;
       return true;
   }

//函數功能:將制定的元素加入到List的指定位置。
public void add(int index, E element) {
       //位置有效性檢查
       rangeCheckForAdd(index);
       //添加修改次數以及判斷是否需要擴張數組長度,
       ensureCapacityInternal(size + 1);  // Increments modCount!!
       //完成數組自身從index開始的所有元素拷貝到index+1開始且長度爲size-index的位置上。
       System.arraycopy(elementData, index, elementData, index + 1,
                        size - index);
        //插入數據                
       elementData[index] = element;
       //數據量加1
       size++;
   }

在增加數據之前都需要對數組長度進行判斷,下面分析一下數據長度判斷以及擴容的函數ensureCapacityInternal


private void ensureCapacityInternal(int minCapacity) {
        //如果是剛構建的ArrayList,(數據長度則取10與minCapacity的最大值)
        //調用無參構造相當於構造的一個長度爲10的數組對象。
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

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

        // overflow-conscious code
        //數組長度不夠,需要擴容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

//ArrayList擴容函數
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //一般情況下是擴展到原來數組長度的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
         /*下面兩個if的作用爲處理兩種情況:
        1)第一種情況爲:如果newCapacity擴展的過小。則應該至少擴張到所需的空間大小minCapacity
        2)第二種情況爲:newCapacity擴張的過大,如果過大,則用Integer.MAX_VALUE來代替。*/

        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:
        //原來的數組中的元素複製擴展到大小爲newCapacity的新數組中,並返回這個新數組
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

addAll函數

public boolean addAll(Collection<? extends E> c) {
        //轉爲數組
        Object[] a = c.toArray();
        int numNew = a.length;
        //判斷是否擴容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //複製集合數組到ArrayList數組中
        System.arraycopy(a, 0, elementData, size, numNew);
        //改變長度
        size += numNew;
        return numNew != 0;
    }

public boolean addAll(int index, Collection<? extends E> c) {
        //判斷插入位置是否正常
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
         //判斷是否擴容
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        //先將Array數據從複製到後面
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
        //複製集合數組到ArrayList數組中
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

remove函數

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

        modCount++;
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        //數組從index開始往左移動一位
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //最後一個設置爲null,便於回收
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

public boolean remove(Object o) {
        if (o == null) {
            //遍歷尋找對象,然後刪除(只刪除第一個匹配的元素)
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    //和remove一樣,只是少了位置校驗
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

get函數

public E get(int index) {
        //判斷位置是否合法
        rangeCheck(index);
        //直接返回即可
        return elementData(index);
    }

set函數

public E set(int index, E element) {
        //判斷位置是否合法
        rangeCheck(index);

        E oldValue = elementData(index);
        //更新數據
        elementData[index] = element;
        //返回老數據
        return oldValue;
    }

clear函數

public void clear() {
        modCount++;

        // clear to let GC do its work
        //遍歷依次置爲null
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

小結
發現學了很多都忘記了,還是需要統一整理一下,然後做一下筆記。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章