ArrayList是存儲一組數據的集合,底層也是基於數組的方式實現,實際上也是對數組元素的增刪改查;它的主要特色是:java
ArrayList類結構以下圖:數組
咱們能夠看到ArrayList實現了List、RandomAccess、Cloneable、java.io.Serializable四個接口和繼承了一個AbstractList抽象類,下面咱們就來進一步的分析它們各自的做用;安全
下面咱們主要從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
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); // 建立一個新的容量數組賦值給存儲數組 }
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) }
public E set(int index, E element) { rangeCheck(index); // 檢驗索引是否合法 E oldValue = elementData(index); // 原始值 elementData[index] = element; // 把存儲數組索引爲index的值改成新值 return oldValue; // 返回原始值 }
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 是個動態數組,插入元素時須要移動指針操做,因此插入元素效率低。
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。
若是有不足之處請指出,謝謝!