ArrayList 定義
ArrayList 是一個用數組實現的集合,支持隨機訪問,元素有序且可以重復。

1 public class ArrayList<E> extends AbstractList<E> 2 implements List<E>, RandomAccess, Cloneable, java.io.Serializable
藍色實線箭頭是指Class繼承關系
綠色實線箭頭是指interface繼承關系
綠色虛線箭頭是指接口實現關系
由上可知ArrayList繼承AbstractList 並且實現了List和RandomAccess,Cloneable, Serializable接口。
①、實現 List 接口
List 接口定義了實現該接口的類都必須要實現的一組方法,如下所示,下面我們會對這一系列方法的實現做詳細介紹。
字段屬性

//集合默認大小 private static final int DEFAULT_CAPACITY = 10; //空的數組實例 private static final Object[] EMPTY_ELEMENTDATA = {}; //這也是一個空的數組實例,和EMPTY_ELEMENTDATA空數組相比是用於了解添加元素時數組膨脹多少 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //存儲 ArrayList集合的元素,集合的長度即這個數組的長度 //1、當 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時將會清空 ArrayList //2、當添加第一個元素時,elementData 長度會擴展為 DEFAULT_CAPACITY=10 //3、transient修飾表示序列化對象的時候,這個屬性就不會序列化到指定的目的地中;通過方法來手動序列化,下面講解 transient Object[] elementData; //表示集合的長度 private int size;
構造函數
①、無參構造函數

public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
此無參構造函數將創建一個 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 聲明的數組,注意此時初始容量是0,而不是大家以為的 10。
注意:根據默認構造函數創建的集合,ArrayList list = new ArrayList();此時集合長度是0.
②、給定大小有參構造函數

1 public ArrayList(int initialCapacity) { 2 if (initialCapacity > 0) { 3 this.elementData = new Object[initialCapacity]; 4 } else if (initialCapacity == 0) { 5 this.elementData = EMPTY_ELEMENTDATA; 6 } else { 7 throw new IllegalArgumentException("Illegal Capacity: "+ 8 initialCapacity); 9 } 10 }
初始化集合大小創建 ArrayList 集合。當大於0時,給定多少那就創建多大的數組並賦給賦給elementData;當等於0時,將空的數組實例EMPTY_ELEMENTDATA 賦給elementData;當小於0時,拋出異常。
③、泛型參數有參構造函數

public ArrayList(Collection<? extends E> c) { //將collection對象轉換成數組,然后將數組的地址的賦給elementData。 elementData = c.toArray(); //更新size的值,並且如果size大於0 if ((size = elementData.length) != 0) { if (elementData.getClass() != Object[].class) //把collection對象的內容copy到elementData中 elementData = Arrays.copyOf(elementData, size, Object[].class); } else { //size等於0直接將空對象EMPTY_ELEMENTDATA的地址賦給elementData this.elementData = EMPTY_ELEMENTDATA; } }
Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素。?是“任意類”的意思,extends繼承不多說,E是指定類型。
用法舉例:

List stu=new ArrayList<Student>(); stu.add(new Student()); List students=new ArrayList<Student>(stu);
添加元素

public boolean add(E e) { //添加元素之前,首先要確定集合的大小,如果集合不夠裝則需要擴容 //第一次size=0 ensureCapacityInternal(size + 1); //集合size位置賦值,先引用size的值,再對size自增 elementData[size++] = e; return true; }

private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //第一次添加,空集合,10和1取最大值,所以第一次添加元素初始集合length為10 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //擴容判斷 ensureExplicitCapacity(minCapacity); }

private void ensureExplicitCapacity(int minCapacity) { modCount++; //如果size+1大於集合的大小,說明剛好集合已經滿了,則需要擴容 //size 是集合實際元素個數,elementData.length是集合長度,元素沒滿,用null填滿 if (minCapacity - elementData.length > 0) grow(minCapacity); }

private void grow(int minCapacity) { //集合的容量 int oldCapacity = elementData.length; //>>1表示右移位,相當於除以2的n次方,這里除以2的1次方,相當於容量增加原來容量的一半 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) //擴容一半后還比minCapacity小,那就只有oldCapacity=0的情況了,也就是第一次添加元素,集合大小為10 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) //擴容容量大於最大集合大小 newCapacity = hugeCapacity(minCapacity); //數組的復制 elementData = Arrays.copyOf(elementData, newCapacity); }

private static int hugeCapacity(int minCapacity) { //Integet.MAX_VALUE+1時就會溢出,這時會變成負數,Integer.MAX_VALUE+1=Integer.MIN_VALUE if (minCapacity < 0) throw new OutOfMemoryError(); //size+1比最大集合大小還大就取最大整數,否則取最大集合大小 //所以先擴容到MAX_ARRAY_SIZE,下一次擴容就擴容到Integer.MAX_VALUE return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }

public static <T> T[] copyOf(T[] original, int newLength) { return (T[]) copyOf(original, newLength, original.getClass()); } public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") // 判斷類型是否為Object,是Object,生成一個大小為newLength類型為Object的數組實例 T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength);// 類型不為Object,通過反射重新生成一個大小為newLength的新類型數組實例 ///將原數組內容拷貝到新數組中,新數組取最小的數組長度,這里指copy的數組的長度,是原數組的長度 System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } //native關鍵字表示系統方法,Java可以通過JNI來調用其他語言(主要還是C/C++語言)編寫的方法 /*Object src : 原數組 int srcPos : 從元數據的起始位置開始 Object dest: 目標數組 int destPos: 目標數組的開始起始位置 int length : 要copy的數組的長度 */ public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
①、當通過 ArrayList() 構造一個空集合,初始長度是為0的,size也為0,第 1 次添加元素,會創建一個長度為10的數組,並將該元素賦值到數組的第一個位置,下標為0的位置。
②、第 2 次添加元素,集合不為空,而且由於集合的長度size+1是小於數組的長度10,所以直接添加元素到數組的第二個位置,不用擴容。
③、第 11 次添加元素,此時 size+1 = 11,而數組長度是10,這時候創建一個長度為10+10*0.5 = 15 的數組(擴容1.5倍),然后將原數組元素引用拷貝到新數組。並將第 11 次添加的元素賦值到新數組下標為10的位置。
④、當第一次擴容容量 oldCapacity + (oldCapacity >> 1)大於最大集合大小Integer.MAX_VALUE - 8時,判斷size+1和Integer.MAX_VALUE - 8的大小,第一次size+1肯定小,這時擴容到Integer.MAX_VALUE - 8
⑤、當集合size=Integer.MAX_VALUE - 8時,然后添加元素時,創建一個大小為 Integer.MAX_VALUE 的數組,在進行元素添加。
⑥、第 Integer.MAX_VALUE + 1 次添加元素時,拋出 OutOfMemoryError 異常。
注意:能向集合中添加 null 的,因為數組可以有 null 值存在。
查找元素

public E get(int index) { //檢查索引是否越界,越界則拋出異常 rangeCheck(index); //直接返回處於該下標位置的數組元素 return elementData(index); }
修改元素

public E set(int index, E element) { //判斷索引是否越界 rangeCheck(index); //獲取原數組指定索引的元素 E oldValue = elementData(index); //將指定索引處的元素替換為 element elementData[index] = element; return oldValue;//返回原數組索引元素 }
刪除元素
①、通過索引位置刪除

public E remove(int index) { rangeCheck(index);//判斷給定索引的范圍,超過集合大小則拋出異常 modCount++; E oldValue = elementData(index);//得到索引處的刪除元素 int numMoved = size - index - 1; if (numMoved > 0)//size-index-1 > 0 索引處不是最后一個元素 //將數組elementData 的下標index+1之后長度為 numMoved的元素拷貝到從index開始的位置 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; //將數組最后一個元素置為 null //返回刪除的元素 return oldValue; }
②、直接刪除指定元素

public boolean remove(Object o) { if (o == null) {//如果刪除的元素為null for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); //return第一表示返回值,第二表示中止函數往下執行 return true; } } else {//不為null,通過equals方法判斷對象是否相等 for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } //和通過索引刪除元素方法相同 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 }
remove(Object o)方法是刪除第一次出現的該元素。然后通過System.arraycopy進行數組自身拷貝。
注意並不是刪除所有該元素
遍歷集合
①、迭代器 iterator
用法:

ArrayList<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); System.out.print(str+" "); }
interator方法返回一個 Itr 對象,這個類是 ArrayList 的內部類。

public Iterator<E> iterator() { return new Itr(); }

private class Itr implements Iterator<E> { int cursor; //游標,下一個要返回的元素的索引 int lastRet = -1; //上一次返回元素的索引; 如果沒有這樣的話返回-1. int expectedModCount = modCount;//這個值是在用戶調用ArrayList的iterator方法時候確定的 public boolean hasNext() { //通過 cursor != size 判斷是否還有下一個元素 //下面每次獲取一個元素cursor都要+1 return cursor != size; } 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];//返回索引為i處的元素,並將 lastRet賦值為i } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet);//調用ArrayList的remove方法刪除元素 cursor = lastRet;//游標指向刪除元素的位置,本來是lastRet+1的,這里刪除一個元素,然后游標就不變了 lastRet = -1;//lastRet恢復默認值-1,因為上一個返回的元素已經刪除了 expectedModCount = modCount;//expectedModCount值和modCount同步,因為進行add和remove操作,modCount會加1 } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } //expectedModCount這個值是在用戶調用ArrayList的iterator方法時候確定的 //前面在新增元素add() 和 刪除元素 remove() 時,我們可以看到 modCount++。修改set() 是沒有的 //也就是說不能在迭代器進行元素迭代時進行增加和刪除操作,否則拋出異常 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
邏輯上講,迭代時可以添加元素,但是一旦開放這個功能,很有可能造成很多意想不到的情況。
比如你在迭代一個ArrayList,迭代器的工作方式是依次返回給你第0個元素,第1個元素,等等,假設當你迭代到第5個元素的時候,你突然在ArrayList的頭部插入了一個元素,使得你所有的元素都往后移動,於是你當前訪問的第5個元素就會被重復訪問。
java認為在迭代過程中,容器應當保持不變。因此,java容器中通常保留了一個域稱為modCount,每次你對容器修改,這個值就會加1。當你調用iterator方法時,返回的迭代器會記住當前的modCount,隨后迭代過程中會檢查這個值,一旦發現這個值發生變化,就說明你對容器做了修改,就會拋異常。
迭代時不能調用 ArrayList.remove() 方法,可以調用 迭代器的 remove() 方法刪除元素:

ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); System.out.print(str+" "); list.remove(str);//集合遍歷時進行刪除或者新增操作,都會拋出 ConcurrentModificationException 異常 //list.add(str); list.set(0, str);//修改操作不會造成異常 it.remove();//調用迭代器內部刪除方法 }
序列化

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ 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(); } }

private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; s.defaultReadObject(); s.readInt(); if (size > 0) { ensureCapacityInternal(size); Object[] a = elementData; for (int i=0; i<size; i++) { a[i] = s.readObject(); } } }
ArrayList在序列化的時候會調用writeObject,直接將size和element寫入ObjectOutputStream;反序列化時調用readObject,從ObjectInputStream獲取size和element,再恢復到elementData。假如elementData的長度為10,而其中只有5個元素,那么在序列化的時候只需要存儲5個元素,而數組中后面5個元素是不需要存儲的。於是將elementData定義為transient,避免了Java自帶的序列化機制,就可以保證只序列化實際存儲的那些元素,而不是整個數組,從而節省空間和時間。