◆
ArrayList簡介
◆java
ArrayList 是一個數組隊列,至關於 動態數組。與Java中的數組相比,它的容量能動態增加。它繼承於AbstractList,實現了List, RandomAccess, Cloneable, java.io.Serializable這些接口。git
◆
ArrayList的屬性
◆github
1234567891011121314151617181920212223242526272829303132複製代碼 |
/** * 數組默認的大小 */
private static final int DEFAULT_CAPACITY = 10;
/** * 使用數組大小爲0時的默認緩衝區 */
private static final Object[] EMPTY_ELEMENTDATA = {};
/** * 使用ArrayList(int initialCapacity)構造方法時且initialCapacity爲0時緩衝區 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/** * 真實存儲arraylist元素的數組緩衝區 */
transient Object[] elementData;
// non-private to simplify nested class access
/** * List的實際大小 */
private int size;
/** * 數組可分配的最大大小 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/** * 特別注意這個是繼承自AbstractList的屬性,用來記錄List被修改的次數 */
protected transient int modCount = 0;複製代碼 |
◆
ArrayList的構造方法
◆數組
1234567891011121314151617181920212223242526272829303132333435複製代碼 |
/** * 無參構造方法,初始化elementData */
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
/** * 根據參數構建具備初始大小的構造方法 */
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}}/** * 建立一個包含collection的ArrayList */
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else { //replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}}複製代碼 |
◆
ArrayList的方法
◆安全
接下來咱們就以ArrayList的幾個比較經典的方法來看一下它是如何設計的。bash
首先是添加方法,添加的方法一共有3個:多線程
123456789101112131415161718192021222324252627282930313233343536複製代碼 |
/** * 添加元素 */ public boolean add(E e) { //計算數組最新的容量,以及判斷是否須要擴容 ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } /** * 指定索引添加元素 */ public void add(int index, E element) { //判斷索引是否越界 rangeCheckForAdd(index); //計算數組最新的容量,以及判斷是否須要擴容 ensureCapacityInternal(size + 1); // Increments modCount!! //調用系統底層的複製方法 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; //List長度+1 size++; } /** * 添加一個集合 */ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }複製代碼 |
仔細觀察上方三個添加的方法,它們都調用了ensureCapacityInternal方法,這個方法的參數是執行當前添加操做所須要的數組容量。它會根據傳遞的參數來計算數組是否須要擴容,若是須要擴容則完成擴容操做。
不一樣之處在於,上方的兩個方法添加的只有一個元素,因此傳的size+1,而addAll由於是添加的一個集合因此傳的參數是size+集合的長度。dom
接着看這個方法的實現:學習
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152複製代碼 |
/** * 計算數組最新的容量 * @param minCapacity */ private void ensureCapacityInternal(int minCapacity) { //若是建立ArrayList時指定大小爲0 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //若是本次添加的大小比初始容量10大的話則不使用默認的容量10,直接使用本次添加的大小做爲初始容量 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } /** * 記錄修改次數,調用擴容方法 * @param minCapacity */ private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) //擴容 grow(minCapacity); } /** * 擴容 */ private void grow(int minCapacity) { // 獲取原來的數組長度 int oldCapacity = elementData.length; //新容量設置爲老容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //若是新容量還不夠存放本次須要添加的大小,則直接擴容到本次添加的大小 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //若是新容量超出數組最大容量 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 調用Arrays的複製方法更新數據緩衝池 elementData = Arrays.copyOf(elementData, newCapacity); } //判斷容量是否溢出 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }複製代碼 |
以上就是ArrayList動態擴容的實現方式了,這裏注意一下擴容是經過新建一個數組來替換原先的數組來進行的:ui
1複製代碼 |
elementData = Arrays.copyOf(elementData, newCapacity);複製代碼 |
接下來看刪除操做:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152複製代碼 |
/** * 遍歷數組,找出須要刪除的元素的索引,並調用刪除方法 */ public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { //具體刪除方法 fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }/** * 刪除指定索引的元素 * */ 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); //數組長度減一,刪除數組最後一個位置的元素 elementData[--size] = null; // clear to let GC do its work return oldValue; } /* * 刪除指定元素 */ private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }複製代碼 |
須要注意的是刪除一個元素也是經過底層的方法實現的。
接着看get和set相對就比較簡單了。
1234567891011121314151617181920複製代碼 |
public E get(int index) { //判斷索引是否越界 rangeCheck(index); return elementData(index); } /** * 判斷索引是否越界 */ private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } public E set(int index, E element) { //判斷索引是否越界 rangeCheck(index); //獲取此索引原先的值 E oldValue = elementData(index); elementData[index] = element; return oldValue; }複製代碼 |
看了ArrayList的增刪改查方法相信你已經明白了爲何一直有人告訴你ArrayList查詢修改效率高而添加和刪除效率低了。
ArrayList的序列化方式一樣是比較有意思的,一開始看到ArrayList實現了Serializable咱們就知道它是能夠序列化的,可是實際存儲的數組elementData倒是transient,觀看下方代碼你就能夠找到答案:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748複製代碼 |
/** * 將List寫入s,注意先寫容量,而後在寫數據 * @param s * @throws java.io.IOException */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // 首先寫數組容量 s.writeInt(size); // 遍歷寫數組中的元素 for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } /** * 讀取s中的List */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff s.defaultReadObject(); // 首先讀數組容量 s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity ensureCapacityInternal(size); Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i<size; i++) { a[i] = s.readObject(); } } }複製代碼 |
鑑於篇幅有限,本篇文章僅列出上方部分代碼,ArrayList完整源碼解析請看:github.com/shiyujun/sy…!!!