JDK1.8 ArrayList部分源碼分析小記

JDK1.8 ArrayList部分源碼分析小記

底層數據結構

底層的數據結構就是數組,數組元素類型爲Object類型,便可以存放全部類型數據。
咱們對ArrayList類的實例的全部的操做底層都是基於數組的。java

繼承與實現關係

ArrayList繼承的父類爲:AbstractList(抽象類)
實現的接口有:List(規定了List的操做規範)、RandomAccess(可隨機訪問)、Cloneable(可拷貝)、Serializable(可序列化)數組

友情提示:由於ArrayList實現了RandomAccess接口,因此儘可能用for(int i = 0; i < size; i++) 來遍歷而不要用Iterator迭代器來遍歷,後者在效率上要差一些數據結構

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

部分屬性

類的屬性中核心的屬性爲elementData,類型爲Object[],用於存放實際元素,而且被標記爲transient,也就意味着在序列化的時候,此字段是不會被序列化的。dom

友情提示:ArrayList的默認容量爲10函數

// 缺省容量
    private static final int DEFAULT_CAPACITY = 10;
    // 元素數組(調用指定初始值的構造函數時elementData的長度會變成指定值)
    transient Object[] elementData; 
    // 空對象數組
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 缺省空對象數組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

經常使用構造函數

友情提示:建立ArrayList時儘可能設置初始大小(使用ArrayList(int initialCapacity)構造函數)源碼分析

/**
     * ArrayList帶容量大小的構造函數
     *
     * @param initialCapacity
     */
    //說明:指定elementData數組的大小,不容許初始化大小小於0,不然拋出異常。
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {// 初始容量大於0
            this.elementData = new Object[initialCapacity];// 初始化元素數組(新建一個數組)
        } else if (initialCapacity == 0) {// 初始容量爲0
            this.elementData = EMPTY_ELEMENTDATA;// 爲空對象數組
        } else {// 初始容量小於0,拋出異常
            throw new IllegalArgumentException("Illegal Capacity: " +
                    initialCapacity);
        }
    }
    
    /**
     * ArrayList無參構造函數。默認容量是10
     */
    //說明:當未指定初始化大小時,會給elementData賦值爲空集合。
    public ArrayList() {
        // 無參構造函數,設置元素數組爲空
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

經常使用函數分析

友情提示
add方法:當容量到達size時進行擴容(add(E e)中先調用了ensureCapacity(size+1)方法,以後將元素的索引賦給elementData[size],然後size自增),擴容爲當前容量的0.5倍(若果ArrayList的當前容量爲10,那麼一次擴容後的容量爲15)
set方法:調用set方法時會檢驗索引是否合法(只校驗了上限)(index不能等於size(index<size))
get方法:調用get方法時也會檢驗索引是否合法(只校驗了上限)(index不能等於size(index<size))
remove方法:在移除指定下標的元素時,會把指定下標到數組末尾的元素向前移動一個單位,而且會把數組最後一個元素設置爲null,這樣是爲了方便以後整個數組不被使用時,能夠被GC;元素移動時使用的是System.arraycopy()方法測試

/**
     * 添加元素
     *
     * @param e
     * @return
     */
    public boolean add(E e) {
        //確保elementData數組有合適的大小
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }

    /**
     * 設定指定下標索引的元素值
     *
     * @param index
     * @param element
     * @return
     */
    public E set(int index, E element) {
        // 檢驗索引是否合法
        rangeCheck(index);
        // 舊值
        E oldValue = elementData(index);
        // 賦新值
        elementData[index] = element;
        // 返回舊值
        return oldValue;
    }

    /**
     * 獲取指定下標的元素
     *
     * @param index
     * @return
     */
    //說明:get函數會檢查索引值是否合法(只檢查是否大於size,而沒有檢查是否小於0),
    // 值得注意的是,在get函數中存在element函數,element函數用於返回具體的元素 
    public E get(int index) {
        // 檢驗索引是否合法
        rangeCheck(index);

        return elementData(index);
    }

    // Positional Access Operations

    /**
     * 位置訪問操做
     *
     * @param index
     * @return
     */
    //說明:返回的值都通過了向下轉型(Object -> E(泛型))
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

    /**
     * 移除指定下標元素
     *
     * @param index
     * @return
     */
    //說明:remove函數用戶移除指定下標的元素
    // 此時會把指定下標到數組末尾的元素向前移動一個單位,而且會把數組最後一個元素設置爲null,這樣是爲了方便以後將整個數組不被使用時,能夠會被GC。
    //提醒:元素移動時使用的是System.arraycopy()方法
    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);
        // 賦值爲空,有利於進行GC
        elementData[--size] = null; // clear to let GC do its work
        // 返回舊值
        return oldValue;
    }

    /**
     * 在指定下標位置插入元素
     * @param index
     * @param element
     */
    public void add(int index, E element) {
        //檢查索引是否合法
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1); 
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

其餘方法介紹

友情提示:rangeCheckForAdd方法用於add(int index, E element)和addAll(int index, Collection<? extends E> c)方法中檢驗索引是否合法;rangeCheck方法用於get、set等方法中檢驗索引是否合法(由於不改變數據結構,故index不能取到size,最大隻能取到size-1)this

//檢驗索引是否合法(只校驗了上限)(index不能等於size(index<size))
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    //檢驗索引是否合法(校驗了上限和下限)(index能夠等於size(0<=index<=size))
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

擴容策略

// 肯定ArrarList的容量。
    // 若ArrayList的容量不足以容納當前的所有元素,設置 新的容量=「(原始容量x3)/2 + 1」
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                ? 0
                : DEFAULT_CAPACITY;//默認容量10

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

    //確保elementData數組有合適的大小
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 判斷元素數組是否爲空數組
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);// 取較大值
        }
        //確保elemenData數組有合適的大小
        ensureExplicitCapacity(minCapacity);
    }

    //確保elemenData數組有合適的大小
    private void ensureExplicitCapacity(int minCapacity) {
        // 結構性修改加1
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    //對數組進行擴容
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;// 舊容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);// 新容量爲舊容量的1.5倍
        if (newCapacity - minCapacity < 0)// 新容量小於參數指定容量,修改新容量
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)// 新容量大於最大容量
            newCapacity = hugeCapacity(minCapacity);// 指定新容量
        // 拷貝擴容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

部分方法測試

  • add方法
public class AddTest {
    @Test
    public void test1(){
        //咱們能夠看到,在add方法以前開始elementData = {};
        // 調用add方法時會繼續調用,直至grow,最後elementData的大小變爲10,
        // 以後再返回到add函數,把8放在elementData[0]中
        List<Integer> lists = new ArrayList<Integer>();
        lists.add(8);
    }

    @Test
    public void test2(){
        //說明:咱們能夠知道,在調用add方法以前,elementData的大小已經爲6,以後再進行傳遞,不會進行擴容處理。
        List<Integer> lists = new ArrayList<Integer>(6);//elementData.length=6
        lists.add(8);
    }
    
}
  • rangeCheck方法
public class RangeCheckTest {
    @Test
    public void test() {
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        //該語句報ArrayIndexOutOfBoundsException異常是rangeCheck(index)引起的(index >= size)
        System.out.println(list.get(10));
        //rangeCheck(index)方法只校驗上線,該語句報ArrayIndexOutOfBoundsException異常是elementData[index]引起的
        System.out.println(list.get(-1));
        list.remove(-1);//同上

        Object[] a = new Object[]{1, 2, 3};
        System.out.println(a[-1]);
    }
}
  • indexOf方法
public class IndexOfTest {
    @Test
    public void test(){
        List list = new ArrayList();
        list.add(null);
        list.add(2);
        list.add(2);
        list.add(null);
        System.out.println(list.indexOf(null));//0
        System.out.println(list.indexOf(2));//1
        System.out.println(list.indexOf(3));//-1
    }
}
  • toArray方法
public class ToArrayTest {
    @Test
    public void test() {
        List list = new ArrayList(10);
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        Object[] array =list.toArray();
        //調用Arrays.copyOf()-->調用System.arraycopy()
    }
}
相關文章
相關標籤/搜索