ArrayList源碼學習----JDK1.7

什麼是ArrayList?


   ArrayList是存儲一組數據的集合,底層也是基於數組的方式實現,實際上也是對數組元素的增刪改查;它的主要特色是java

  • 有序;(基於數組實現)
  • 隨機訪問速度快;(進行隨機訪問的時候,只須要遍歷全部的數組)
  • 能夠爲null;(基於數組實現)
  • 元素能夠重複;(基於數組實現)
  • 線程是不安全的;(對於多個線程訪問時,沒有進行同步操做,之因此不安全也是爲它的性能而設計,若是須要建立一個安全的ArrayList集合能夠經過 Collections.synchronizedList(list);實現同步)

  

 ArrayList結構分析


 ArrayList類結構以下圖:數組

    

 咱們能夠看到ArrayList實現了List、RandomAccess、Cloneable、java.io.Serializable四個接口和繼承了一個AbstractList抽象類,下面咱們就來進一步的分析它們各自的做用;安全

  • Iterable :實現此接口以便支持foreach語法。
  • Collection:包含了集合基本方法。
  • AbstractCollection:實現了Collection裏面大部分方法。
  • List:繼承自Collection接口,包含了對集合操做的一些方法。
  • AbstractList:繼承自AbstractCollection實現List部分方法。
  • Serializable:序列化的接口。
  • RandomAccess 用來代表其支持快速隨機訪問。
  • Cloneable:標識其支持對象複製。

 

  ArrayList源碼分析


下面咱們主要從ArrayList屬性構造方法核心的方法來進行解析:dom

 

ArrayList定義了兩個私有屬性:源碼分析

    /**
     * 用於存儲數據的數組
     */
    private transient Object[] elementData;

    /**
     * 數組元素的實際個數
     */
    private int size;

第一個屬性主要是存儲元素的一個數組, 第二個屬性是用於記錄數組元素的實際個數,而不是數組的長度。性能

 

ArrayList定義了三個構造方法:學習

    /**
     * 數組初始容量構造方法
     *
     * @param  initialCapacity  
     * @throws IllegalArgumentException 
     *    
     */
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)    //若是指定的初始容量爲負數,將拋出參數異常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];    //指定一個存儲數組初始容量值爲:initialCapacity
    }

    /**
     * 默認構造方法
     */
    public ArrayList() {
        this(10);    //默認給定初始容量爲10
    }

    /**
     * 給定一個參數集合的構造方法
     * @param c 
     * @throws NullPointerException 
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();    //轉爲數組
        size = elementData.length;    //給定size初始值

        if (elementData.getClass() != Object[].class)    // 是否成功轉化爲Object類型數組
            elementData = Arrays.copyOf(elementData, size, Object[].class);    // 不爲Object數組的話就進行復制
    }

ArrayList 核心方法分析this

 

添加元素  add(E e)

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

從源碼中看到在添加元素時調用ensureCapacityInternal方法,這個方法呢就是爲了保證存儲數組的大小夠用,不會致使數組越界,下面具體看下ensureCapacityInternal實現spa

    private void ensureCapacityInternal(int minCapacity) {
        modCount++;    // 結構性修改加1
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)    //添加元素時長度 - 存儲數組長度是否大於0,若是大於0說明數組大小夠用,小於0則須要對存儲數組擴容
            grow(minCapacity);
    }

若是存儲數組須要進擴容操做,那麼就會調用grow方法,這個方法會產生一個新的數組從新賦值給存儲數組,新的的數組長度是原數組的1.5倍。下面具體看下grow實現線程

    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); // 指定新容量
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);  // 建立一個新的容量數組賦值給存儲數組
    }

 

訪問元素  get(index)

    public E get(int index) {
        rangeCheck(index);    // 檢驗索引是否合法

        return elementData(index);
    }

    在根據索引獲取元素時,首先進行索引值的判斷,索引值大於實際size的大小則拋出異常,不然繼續根據索引值獲取存儲數組中的對象,rangeCheck實現

    private void rangeCheck(int index) {  
        if (index >= size)    // 索引值大於實際size的大小則拋出異常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

   根據索引值獲取存儲數組中的對象,elementData實現

    E elementData(int index) {
        return (E) elementData[index];    // 返回存儲數組對應索引的值,向下轉型(Object -> E)
    }

 

修改元素  set(int index, E element)

    public E set(int index, E element) {
        rangeCheck(index);  // 檢驗索引是否合法

        E oldValue = elementData(index);   // 原始值
        elementData[index] = element;     // 把存儲數組索引爲index的值改成新值
        return oldValue;     // 返回原始值
    }

 

插入元素  add(int index, E element)

    public void add(int index, E element) {
        rangeCheckForAdd(index);  // 檢驗索引是否合法

        ensureCapacityInternal(size + 1);  // 須要擴容擴容操做
    // 拷貝到從下標爲index+1位置開始的新的elementData數組中。     // 即將當前位於該位置的元素以及全部後續元素右移一個位置。  
        System.arraycopy(elementData, index, elementData, index + 1,    
                         size - index);
        elementData[index] = element;
        size++;    // 實際元素長度+1
    }

     因爲ArrayList 是個動態數組,插入元素時須要移動指針操做,因此插入元素效率低。

 

刪除指定位置元素   remove(int index)

    public E remove(int index) {
        rangeCheck(index);    // 檢驗索引是否合法

        modCount++;    // 結構性修改加1
        E oldValue = elementData(index);  // 獲取原來的值

        int numMoved = size - index - 1;  // 須要移動的元素的個數
        if (numMoved > 0)
System.arraycopy(elementData, index
+1, elementData, index, numMoved); elementData[--size] = null; // 最後一個元素設置爲空 return oldValue;    // 返回原始值 }

刪除元素時一樣須要移動指針操做,刪除元素效率低。

刪除元素時首先判斷是否爲最後一個元素,若是不是最後一個,就按指定的索引從存儲數組刪除,索引後面全部元素左移一個位置,並把最後一個元素設置爲null以便GC進行回收

 

 總結


不少技術,不僅是隻會用就行,研究它底層實現實際上是頗有趣的一件事,同時技術可以獲得必定的提高。下篇繼續學習LinkedList

若是有不足之處請指出,謝謝!

相關文章
相關標籤/搜索