使用ArrayList時代碼內部發生了什麽(jdk1.7)?
前言
ArrayList(這裏的ArrayList是基於jdk1.7)是在項目中經常使用的集合類,例如我們從數據庫中查詢出一組數據。這篇文章不去剖析它的繼承和實現,只是讓我們知道實例化及增刪改查時它的內部代碼是怎麽實現的。
public class TestList { @Test public void testArrayList(){ List<Integer> list = new ArrayList<>(); for (int i = 0; i < 12; i++) { list.add(i); } } }
實例化
List<Integer> list = new ArrayList<>();
我們先來看看上面這段實例化ArrayList時,內部發生了什麽。
這調用的是ArrayList的無參構造函數,如下
public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
步驟:
- 調用父類的構造函數(AbstractList的構造函數)
- 賦值elementData(一個空數組{})
這裏把ArrayList內部的屬性說明下,一共有DEFAULT_CAPACITY、EMPTY_ELEMENTDATA、elementData、size。其中DEFAULT_CAPACITY和EMPTY_ELEMENTDATA是被定義為static和final的。而elementData是我們添加元素時存儲的數組,size就是這個數組的大小,DEFAULT_CAPACITY就是數組默認的大小,EMPTY_ELEMENTDATA是一個空數組。
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] 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 == EMPTY_ELEMENTDATA will be expanded to * DEFAULT_CAPACITY when the first element is added. */ private transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;
實例化部分就到這裏了,其他有參的這裏就不做介紹,需要的可以自己去看看,接下來看看添加元素
添加元素
for (int i = 0; i < 12; i++) { list.add(i); }
這裏循環添加了12個元素,是為了查看ArrayList的第一次擴容,接下來看看ArrayList裏面是怎麽實現的
add方法
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
add方法中的步驟如下
- 調用ensureCapacityInternal,傳入參數(size+1)
- elementData數組對象賦值
- 返回true
ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }
這個方法中傳入的參數名稱為minCapacity,翻譯為中文就是最小容量。
這個方法首先判斷當前數組對象elementData是不是等於空對象EMPTY_ELEMENTDATA。大家可以看下實例化時elementData對象的賦值(如下)。
public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
如果是,則最小容量minCapacity重新賦值為DEFAULT_CAPACITY和minCapacity中最大的一個數。
然後調用ensureExplicitCapacity方法。斷點內容如下
minCapacity被賦值為DEFAULT_CAPACITY
接下來是ensureExplicitCapacity方法
ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
modCount是ArrayList的父類AbstractList的一個屬性,記錄被修改的次數,也是通過這個觸發fail-fast機制的,這裏不過多說明。
下面就是比較傳入的最小容量minCapacity減去elementData數組的長度是否大於0(也就是最小容量minCapacity是否大於elementData數組的長度),是的話調用grow方法。
grow方法
這個方法就是ArrayList的擴容方法,接下來看看方法內部代碼
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); }
由於是第一次調用add方法,所以minCapacity值為ArrayList的默認容量10,如下
- 接下來定義一個舊容量oldCapacity,值為elementData數組長度
- 然後定義一個新容量newCapacity,值為舊容量oldCapacity加上(舊容量oldCapacity右移1位,也就是除以2,不四舍五入)
- 接著比較新容量newCapacity和傳入的最小容量minCapacity,誰大就賦值給新容量newCapacity
- 如果新容量newCapacity比定義的數組最大容量MAX_ARRAY_SIZE還大的話,就調用hugeCapacity方法,看是否拋出異常還是賦最大值
- 調用Arrays.copyOf進行數組擴容
獲取元素
獲取元素有三種方式
- 叠代器Iterator遍歷
- 通過索引
- foreach循環獲取
這裏就介紹下通過叠代器Iterator遍歷吧。
public Iterator<E> iterator() { return new Itr(); }
Itr是ArrayList一個實現Iterator的內部類,通過這個對象來獲取ArrayList中存儲的元素。代碼如下
/** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
總結
當我們實例化ArrayList時(調用無參構造函數),這個對象裏面存儲元素的數組還只是一個空數組。
在第一次調用add方法時(也就是第一次添加元素時),對存儲元素的數組elementData進行第一次擴容,擴容的數組長度為ArrayList內部定義的默認容量10,。
當插入第11個數組元素時,進行第二次擴容,擴容的長度為原先的容量加上(原先容量/2),即原先容量的1.5倍。擴容的長度規則是10>>15>>22>>33...
使用ArrayList時代碼內部發生了什麽(jdk1.7)?