ArrayList原理分析及手寫ArrayList

       在面試過程中,會遇到ArrayList實現原理是怎麼樣,在什麼情況會對集合進行擴容等問題;ArrayList相對於其它的集合是比較簡單的了。在後面會將手寫的ArrayList代碼附上,在這之前我們需要了解ArrayList一些主要的成員變量以及原理。

1、主要的成員變量解釋

   /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  • DEFAULT_CAPACITY :集合默認的初始大小
  • EMPTY_ELEMENTDATA: 默認一個空的數組對象;在調用有參的ArrayList時會使用的,比如new ArrayList(int initialCapacity)時,initialCapacity等於0的時候,就會賦值給elementData了。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA :默認一個空的數組對象,從使用程度來看,主要是區分在集合實例化的方式不一樣,在使用默認無參的構造方法時,會將DEFAULTCAPACITY_EMPTY_ELEMENTDATA賦值給elementData;這個跟EMPTY_ELEMENTDATA都是一個空的數組對象,不是很明白爲什麼要搞兩個空的數組對象
  • elementData :元素存放的位置;從這裏可以看出ArrayList實際是用數組存儲元素的
  • size :集合存放的元素大小

2、集合擴容原理

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

這是大家熟悉的ArrayList對象中的add方法;在這個方法中會分一下步驟來進行:

 

  • 計算當前最新的容量;會拿當前即將要添加的元素下標去ensureCapacityInternal(minCapacity)方法中做數組擴容判斷。我們去看跟數組擴容相關的方法,不必要的代碼就直接忽略
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);
    }

在ensureExplicitCapacity方法中"minCapacity - elementData.length"這段代碼纔是擴容的條件。當計算出來的最新下標值減去當前數組的長度大於0時,就滿足擴容條件了。

而在grow方法中,就是進行擴容的邏輯int newCapacity = oldCapacity + (oldCapacity >> 1);主要代碼,這一段代碼就是決定數組要擴容到多少,簡單來說就是:當前的數組長度+(當前數組長度右移1位);結合前面說的DEFAULT_CAPACITY變量,集合默認的數組大小是10,對號入座之後應該是:int newCapacity = 10 + (10 >> 1) 最終結果是15,最後通過Arrays.copyOf對數組進行拷貝擴容。在Arrays.copyOf方法中底層實際是調用管理System.arraycopy方法的,這裏不繼續講解,就當是額外的話題。最終將擴容後的數組賦值給elementData,最後通過elementData[size++] = e;將元素存放到數組中。

總結:ArrayList實際是用數組存儲元素,這也決定的了對隨機獲取元素時,效率是很高的;但是ArrayList對元素的增刪改性能損耗是比較大的因爲增加或者刪除元素,都需要移動集合裏面的元素,因此ArrayList不適合頻繁的增刪改操作,比較適合元素的獲取。

以上就是對ArrayList擴容的講解,其它的方法也相對簡單,這裏就直接忽略了,有興趣的同學可以自行去研究。接下來就是依葫蘆畫瓢,簡單自己實現一個ArrayList的代碼編寫了。


3、手寫ArrayList

import java.util.Arrays;

/**
 * @Description: 自定義ArrayList
 * @CreatedDate: 2018/12/10 15:17
 * @Author: 
 */
public class CustomArrayList<E> {
     /**
      * 初始容量大小
      */
     private static final int DEFAULT_CAPACITY = 10;
     /**
      * 默認一個空的數組
      */
     private static final Object[] DEFAULT_EMPTY_ELEMENTDATA = {};
     /**
      * 數組最大的一個臨界值
      */
     private static final int MAX_SIZE = Integer.MAX_VALUE - 8;
     /**
      * 實際存放的元素值
      */
     private Object[] elementData;
     /**
      * 集合大小
      */
     private int size;

     public CustomArrayList() {
          this.elementData = DEFAULT_EMPTY_ELEMENTDATA;
     }

     /**
      * @Description: 添加元素
      * @Author: 
      * @CreatedDate: 2018/12/10 15:58
      * @param
      * @return
      */
     public boolean add(E e) {
          //計算容量,是否需要擴容
          checkExpansionCapacity(size + 1);
          elementData[size++] = e;
          return true;
     }
     /**
      * @Description: 獲取集合元素:獲取元素之前,先檢查是否會出現數組下標越界
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     public E get(int index) {
          rangeCheck(index);
          return elementData(index);
     }
     /**
      * @Description: 根據下標刪除元素
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     public E remove(int index) {
          rangeCheck(index);
          E oldValue = elementData(index);
          resetEmpty(index);
          return oldValue;
     }
     /**
      * @Description: 根據對象刪除元素
      * @Author: 
      * @CreatedDate: 2018/12/10 18:10
      * @param
      * @return
      */
     public boolean remove(E e) {
          if (e == null) {
               //null,則查詢是否有null的數據,有則刪除
               for (int i =0 ;i <elementData.length; i++) {
                    if (elementData[i] == null) {
                         resetEmpty(i);
                         return true;
                    }
               }
          }else{
               for (int i =0 ;i <elementData.length; i++) {
                    if (e.equals(elementData[i])) {
                         resetEmpty(i);
                         return true;
                    }
               }
          }

          return false;
     }
     /**
      * @Description: 集合大小
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     public int size(){
          return this.size;
     }
     /**
      * @Description: 刪除某一個元素 or 將某一個元素置空
      * System.arraycopy方法參數解釋:
      *    Object src : 原數組
      *    int srcPos : 從元數據的起始位置開始
      *   Object dest : 目標數組
      *   int destPos : 目標數組的開始起始位置
      *   int length  : 要copy的數組的長度
      *
      * @Author: 
      * @CreatedDate: 2018/12/10 18:17
      * @param
      * @return
      */
     private void resetEmpty(int index){
          int movedIndex = size - index - 1;
          //利用底層提供的數組拷貝api來實現拷貝功能
          if (movedIndex > 0)
               System.arraycopy(elementData, index + 1, elementData,  index, movedIndex);
          elementData[--size] = null;
     }

     /**
      * @Description: 獲取數組中元素
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     private E elementData(int index) {
          return (E) this.elementData[index];
     }
     /**
      * @Description: 範圍檢查
      * @Author: 
      * @CreatedDate: 2018/12/10 16:37
      * @param
      * @return
      */
     private void rangeCheck(int index) {
          if (index >= size)
               throw new IndexOutOfBoundsException("index:" + index + ", size:" + size);
     }

     /**
      * @Description: 檢查數組是否需要擴容
      * @Author: 
      * @CreatedDate: 2018/12/10 16:05
      * @param
      * @return
      */
     private void checkExpansionCapacity(int minCapacity){
          int capacity = calculateCapacity(elementData, minCapacity);
          if (capacity - elementData.length > 0)
               expansionCapacity(capacity);
     }
     /**
      * @Description: 擴容數組:
      * @Author: 
      * @CreatedDate: 2018/12/10 16:05
      * @param
      * @return
      */
     private void expansionCapacity(int minCapacity){
          int oldCapacity = elementData.length;
          int newCapacity = oldCapacity + (oldCapacity >> 1);
          if (newCapacity - minCapacity < 0)
               newCapacity = minCapacity;
          if (newCapacity - MAX_SIZE > 0)
               newCapacity = MAX_SIZE;
          //進行數組擴容
          elementData = Arrays.copyOf(elementData, newCapacity);
     }
     /**
      * @Description: 計算容量
      * @Author: 
      * @CreatedDate: 2018/12/10 15:46
      * @param
      * @return
      */
     private int calculateCapacity(Object[] elementData, int minCapacity){
          if (elementData.getClass() == DEFAULT_EMPTY_ELEMENTDATA.getClass()) {
               //一開始數組是空的,則計算出最大的容量值
               return Math.max(DEFAULT_CAPACITY, minCapacity);
          }
          return minCapacity;
     }

}


public class ArrayListDemo {

     public static void main(String[] args) {
          CustomArrayList<String> list = new CustomArrayList<String>();
          list.add("test1");
          list.add("test2");
          list.add("test3");
          list.add("test4");
          list.add("test5");
          for (int i= 0; i< 8; i ++ ) {
               if(i == 5) {
                    System.out.println("start...");
               }
               list.add("demo_" + i);
          }
          System.out.println(list.size());
          list.remove(0);
          list.remove("test2");
          String result = list.get(1);
          System.out.println(result);
          System.out.println(list.size());
     }

}

以上內容如有問題,希望可以留言給我,我可以及時糾正。很高興與您相遇,希望對閱讀完後對您有所幫助;

學習永無止境,不進則退~!

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