ArrayList原始碼分析-java8
阿新 • • 發佈:2019-02-05
1.特點總結:
- 可儲存元素null
- 呼叫無參構造器建立例項,預設容量capacity=10
- 自動擴容倍數:1.5
- 和Vector類等價,區別是 ArrayList不是執行緒安全的.
4個重要的private static final型別例項變數:
- EMPTY_ELEMENTDATA
- 空例項的共享空陣列
- 在capacity=0時的共享,令elementData=EMPTY_ELEMENTDATA。
- 呼叫指定capacity構造器引數方法時,如果capacity=0,則預設的陣列就是這個
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- 預設capacity空例項的共享空陣列
- 呼叫無引數構造器時,初始化的陣列就是預設的這個。
- 與上面變數的區別:在新增第一個元素時要擴容多少。
- DEFAULT_CAPACITY
- 預設的容量10
- MAX_ARRAY_SIZE
- 表示在java中,陣列能分配的的最大儲存空間,值為:Integer.MAX_VALUE - 8
- EMPTY_ELEMENTDATA
區分size和capacity
- size<=capacity
- size表示ArrayList中儲存了幾個元素
- capacity表示當前elementData陣列的長度
- java8新增方法:
- 迭代器中:
- forEachRemaining
- forEach
- 分割器中:
- tryAdvance
- forEachRemaining
- removeIf
- replaceAll
- 迭代器中:
2.grow方法的自動擴容過程
3.原始碼分析
package sourcecode.analysis;
/**
* Created by caoxiaohong on 17/11/6 17:51.
*/
import java.util.*;
import java.util.function.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* ArrayList實現了List介面,是一個大小可變的陣列.
* 它實現了list所有的可選操作,可儲存任意元素,包括null.
* 除了實現了List介面,這個類還提供了一些運算元組大小的方法,用於內部儲存這個list.
* 這個類可大致認為和Vector類等價,區別是 ArrayList不是執行緒安全的.
*
* 可在常量時間內完成的操作包括:size, isEmpty, get, set, iterator, listIterator.
* 而add操作的時間複雜度為:分期常量時間,也就說,adding一個元素需要O(n)的時間.
* 大致上,其它所有的操作執行時間都是線性的.
* 和LinkedList相比,這一常量因素還是很低的.
*
* 每一個ArrayList例項都有一個容量.
* 容量表示了list中儲存元素的陣列大小,它至少應該和list的size大小一致.
* 當一個元素被add到ArrayList中,它的capacity是自動增長的.
* 關於增長策略的細節:唯一可以確定的是新增一個元素的時間代價是常量,其它一概是不確定的.
*
* ArrayList例項在宣告時,如果使用了確定capacity大小的操作,那麼這一應用能夠提升這個ArrayList例項的容量.這
* 可能會降低自動擴容的次數.
* 說明1:
* Q:提升容量是什麼意思?
* A:ArrayList在new的時候,如果沒有指定大小,那麼會有一個預設大小的capacity,這個值為10,而指定了capacity後,capacity會改變為指定值大
* 小。指定capacity的這一操作,會減少為ArrayList重新分配記憶體的次數(這樣效能會得到比較好的優化).
*
* 說明2:
* Q:為什麼會出現為ArrayList重新分配記憶體的時候?
* A:因為ArrayList在新增元素後,如果size+1>capacity,則會重新分配記憶體。
*
* 注意ArrayList是非執行緒安全的.如果多個執行緒同時作用於同一個ArrayList例項中的某個共同的元素,並且至少有一個執行緒更改了list的結構,那麼
* 我們必須為其新增額外的操作以達到執行緒安全的目的.這一操作,通常是通過同步封裝在list中的幾個元素來完成的(而不是讓整個list中的元素
* 都進行同步操作).
* 更改list結構的操作有:
* (1)add 或者 delete 一個或者多個元素;
* (2)明確更改後臺陣列大小的操作;
* 注意:僅僅更改ArrayList中某個元素的值並不屬於更改list結構的操作.
*
* 如果沒有這樣的物件存在,list應該使用Collections.synchronizedList這個方法將其封裝成執行緒安全的.這個封裝操作最好在list建立時候就完成,
* 以避免在list中出現一些偶然的執行緒非同步現象.
* 封裝方法如下:
* List list = Collections.synchronizedList(new ArrayList(...));
*
* ArrayList通過方法iterator()和listIterator(int)這兩個方法返回的迭代器都會盡可能早的丟擲異常:(所謂儘可能早的是意思是:)
* 只要是迭代器建立之後,無論在何時,採用何種方式,只要list結構發生更改,迭代器都會丟擲ConcurrentModificationException異常.
* 注意:迭代器自己的操作remove和add則不會丟擲上述異常.
* 因此,面對併發修改(list結構),迭代器會迅速丟擲異常而不能繼續使用,而不是去冒一種在未來不確定的某時,發生一些不確定行為的風險.
*
* 注意:迭代器的這種儘可能早的丟擲異常的行為並不是在出現上述更改list結構時,一定會出現的,一般來講,在出現了非執行緒安全的併發修改list結構
* 這種現象時,不能硬性保證一定會丟擲異常.
* 迭代器的的Fail-fast功能,只是儘可能的丟擲ConcurrentModificationException這一異常.
* 因此,如果一個程式的正確性完全依賴Fail-fast這一異常的話,這是一種錯誤的方式:迭代器的Fail-fast行為僅僅應該被用來查詢bug.
*
* @author Josh Bloch
* @author Neal Gafter
* @see Collection
* @see List
* @see LinkedList
* @see Vector
* @since 1.2
*/
/**
* 從類名處分析:
* (1)ArrayList<E>:表明ArrayList支援泛型
* (2)繼承1個類:AbstractList 繼承了AbstractCollection類,並且含概了List介面方法的預設實現.
* (3)實現4個介面:
* List:定義了列表必須實現的操作方法.
* RandomAccess:這是一個標記介面,接口裡面沒有任何方法和欄位.這一介面存在的意義:實現了這一介面的類支援隨機訪問元素.對於一個被訪問的列表來說,
* 不管是隨機訪問還是順序訪問,這一介面的原始目的就是允許其通過泛型演算法來更改其行為,來達到更好的效能.
* Cloneable: 接口裡面沒有任何方法和欄位,實現了這個介面的類,才能使得呼叫java.lang.Object.clone()方法才是合法的.但是當前類必須通過override
* Object的clone()這個方法,才能使得這一功能得到實現.這個方法會返回當前類例項的一份淺拷貝.關於淺拷貝:比如ArrayList的
* clone()方法:只拷貝了其儲存內容和當前例項list結構的修改次數modCount,同時modCount在拷貝時被置為0.
* java.io.Serializable:這也是一個裡面沒有任何方法和欄位的介面,只有實現這一介面的類才允許被序列化.沒有實現這一介面的類不允許序列化和反序
* 列化.如果一個類的父類是序列化的,那麼這個子類自然也是可以序列化的.
* @param <E>
*/
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//接下來是5個private型的例項變數,且前3個為常量.且前4個不會被序列化.
/**
* serialVersionUID:是JVM對位元組流進行反序列化的標準.
* 如果被反序列化的位元組流中的serialVersionUID和本地相應的java實體或者物件一致,則當然輸入位元組流可以進行反序列化;否則,會丟擲InvalidClassException異常.
*/
private static final long serialVersionUID = 8683452581122892189L;
/**
* 預設的容量10,也就是在add操作不超過10個時, ArrayList不會進行擴容,超過後才進行擴容.
* 嚴重區別:capacity和size的區別
* capacity表示:在ArrayList擴容前,一共能容納多少個元素.也就是底層陣列的length
* size:表示ArrayList當前真正儲存了幾個元素.
* 關係:capacity>=size
*/
private static final int DEFAULT_CAPACITY = 10;
//空例項的共享空陣列.
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 預設size空例項的共享空陣列
* 我們將這與EMPTY_ELEMENTDATA區分開來,以知道在新增第一個元素時要擴容多少。
* 這一變數是JDK8新增的
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* elementData是ArrayList的元素陣列緩衝區,這是add操作的儲存位置.
* ArrayList的容量是這個陣列緩衝區的長度。
* 當新增第一個元素時,任何具有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* 的空ArrayList將容量擴為:10=DEFAULT_CAPACITY
* 這一變數序列化時,為null
*
*/
private transient Object[] elementData;
//ArrayList 實際儲存了幾個元素
private int size;
/*---------ArrayList的3個建構函式----------*/
//構造方法傳入預設的容量capacity,設定預設陣列大小為指定capacity的大小.
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { //初始化容量指定為0,則用EMPTY_ELEMENTDATA陣列
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//建立一個預設容量為10的空陣列例項,用到了java8新增變數DEFAULTCAPACITY_EMPTY_ELEMENTDATA.
public ArrayList() {
super();
//jdk7程式碼:this.elementData=EMPTY_ELEMENTDATA
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 構造方法傳入一個Collection,再將Collection中的元素copy到ArrayList的陣列elementData中.
* 如果通過Collection得到的陣列elementData型別不是Object[]型別,則將其轉為Object[]型別.
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
//反射,獲取陣列型別,判定c.toArray型別是否為Object[]型別
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 縮減ArrayList例項的capacity為當前儲存元素個數的大小.
* 這一方法的存在意義:如果capacity被分配過大,那麼應用可以通過這個操作,將ArrayList例項的capacity的大小改為當前ArrayList例項
* 儲存元素的個數,從而達到縮減ArrayList儲存空間大小的目的.
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
/**
* 如果需要,增加此ArrayList例項的容量,以確保它至少能容納最小容量引數指定的元素數量。
* 兩種表示式:
* 如果add的是一個元素,則minCapacity=size+1;
* 如果add的是一個Collection,則minCapacity=size+Collection.size;
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
//最小擴容量
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// 如果elementData不是預設空陣列,則擴容大小任意,這裡預設為0
? 0
//如果elementData是預設空陣列,則擴容預設為10
: DEFAULT_CAPACITY;
//如果最小容量>最小擴容量,則可能進行擴容,是否擴容取決於elementData.length
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData ==DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//最小容量:max(10,minCapacity)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//針對最小容量,決定是否擴容
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
/**
* 增加元素後,ArrayList中應該儲存的元素個數為minCapacity,
* 所以,如果此時minCapacity>後臺陣列的長度(elementData.length),則要按照minCapacity進行擴容啦
*/
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* MAX_ARRAY_SIZE:表示在java中,陣列能分配的的最大儲存空間.
* 一些虛擬機器會在陣列中保留一些標題字.
* 如果嘗試分配比MAX_ARRAY_SIZE更大的儲存空間,可能會導致記憶體溢位異常:請求陣列大小超過了虛擬機器的限制.
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 根據給定傳入引數:最小capacity,來為ArrayList進行擴容.
* 這是面試中經常問到的問題:ArrayList是如何擴容的?其實程式碼如此簡短且易理解.
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
//獲取原後臺陣列的長度
int oldCapacity = elementData.length;
/**注意:這裡就是所謂的按照1.5倍進行擴容的思想.顯然如果如果原陣列長度為偶數,
* 那麼新陣列長度就恰好是原後臺陣列的1.5倍;如果原後臺陣列的長度為奇數,則新陣列長度應該比1.5倍少一個.**/
//新陣列的長度=原後臺陣列的長度+原後臺陣列的長度/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果按照1.5倍進行擴容後,capacity仍然比實際需要的小,則新陣列的長度由原來的1.5倍 更改為 實際需要的大小minCapacity.
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果新陣列的長度比虛擬機器能夠提供給陣列的最大儲存空間大,則將新陣列長度更改為最大正數:Integer.MAX_VALUE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 所謂擴容,就是按照新的長度newCapacity建立一個新陣列並返回,然後再將原陣列中的內容copy到新陣列中.
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中儲存的元素個數
public int size() {
return size;
}
//根據ArrayList中是否有儲存的元素,返回true或者false.根據size==0來判定的.
public boolean isEmpty() {
return size == 0;
}
/**
* 檢視ArrayList中是否存在指定元素.
* 存在指定元素1個及以上,則返回true;
* 否則,返回false;
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
*
* 這個方法唯一需要注意的:將查詢物件o分為兩種情況來查詢:
* (1)如果o為null,用==
* (2)如果o為Object,用equals
* 因為:==比較的是地址或者是常量,而equals比較的是物件的內容.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//此方法功能沒什麼好說的,注意事項也和上面的方法一樣.
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
/**
* 這是對ArrayList例項的一個淺拷貝,即將ArrayList例項中的內容拷貝到一個新的ArrayList中,
* 並且新的ArrayList中的操作不會對原ArrayList產生影響.
*/
public Object clone() {
try {
ArrayList<E> v = (ArrayList<E>) super.clone(); //內容拷貝
v.elementData = Arrays.copyOf(elementData, size); //為新的ArrayList的後臺陣列賦值
v.modCount = 0; //為新的ArrayList的結構更改次數字段賦值為0.
return v; //返回新陣列.
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
/**
* 這又是一個淺拷貝的方法.將後臺陣列elementData中的內容賦值為一個新的Object[],並返回.
* Object[]中元素的順序和原後臺陣列elementData中的一致.
* 對新的Object[]的操作不會影響後臺陣列elementData.
* 這一方法被看作是:連線陣列和集合的橋樑.
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
/**
* 功能:將ArrayList轉為陣列
* 方法返回型別:和a一致.
* 方法返回陣列大小:和a的大小有關.
* 如果a.length<size,則返回陣列大小為size.
* 如果a.length>=size,則返回陣列大小為a.length.
*
* 是否一定生成一個新陣列:不一定.
* 如果a.length<size,則會生成一個新的陣列,並返回.
* 如果a.length>=size,則不會生成新的陣列.
*/
public <T> T[] toArray(T[] a) {
if (a.length < size)
//產生一個新的執行時陣列,但陣列內容不變
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
//如果a.length>=size,則通過系統拷貝將a中元素拷貝到elementData中
System.arraycopy(elementData, 0, a, 0, size);
//如果a.length>size,則將a[size]=null,通知jvm可回收此空間
if (a.length > size)
a[size] = null;
return a;
}
/**
* 陣列的隨機訪問:
* 將訪問封裝為方法的目的:
* 主要是避免每次取值都要強轉===>設定值就沒有封裝成一個方法,因為設定值不需要強轉
* @param index
* @return
*/
// 指定位置訪問操作
E elementData(int index) {
return (E) elementData[index];
}
//返回後臺陣列elementData[index]的值.
public E get(int index) {
//索引合法性判定
rangeCheck(index);
return elementData(index);
}
//替換陣列elementData指定位置index的值為element
public E set(int index, E element) {
//索引合法性判定
rangeCheck(index);
//舊值儲存
E oldValue = elementData(index);
elementData[index] = element;
//返回舊值
return oldValue;
}
//在陣列elementData結尾新增一個元素
public boolean add(E e) {
//容量合法性判定
ensureCapacityInternal(size + 1); // 結構更改次數:modCount更改
elementData[size++] = e;
return true;
}
//在後臺數組elementData指定位置index處新增元素element.
public void add(int index, E element) {
//索引合法性判定
rangeCheckForAdd(index);
//容量合法性判定
ensureCapacityInternal(size + 1); //結構更改次數:modCount更改
//系統拷貝
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* 將後臺陣列elementData指定位置index處的元素刪除,然後左移索引index後面的元素.
* 並將刪除元素作為結果返回.
*/
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);
//注意這裡啦:null的賦值,為JVM進行GC做了準備工作
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* 刪除ArrayList中指定的元素,如果這個元素在ArrayList中存在多個,則只刪除最先出現的那個.
* 如果不存在,返回結果false,表示刪除失敗.
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);//呼叫快速remove方法
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);//呼叫快速remove方法
return true;
}
}
return false;
}
//私有快速remove方法,跳過邊界檢查,且不返回舊值
private void fastRemove(int index) {
//增加結構更改次數
modCount++;
//右移元素個數
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//通知JVM進行可用對其進行GC操作
elementData[--size] = null; // clear to let GC do its work
}
/**
* 清空後臺陣列elementData中的元素.
* 它會將elementData中所有的元素置為null.
* 並且重新設定size為0.
*/
public void clear() {
modCount++;
//注意這裡啦:null的賦值,為JVM進行GC做了準備工作.
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
/**
* 將集合c中的元素順次新增到ArrayList例項尾部.
* 這裡注意一個問題:因為ArrayList是非執行緒安全的,所以,如果在將c中的元素新增到ArrayList中時,
* c結構被更改了,這可能會出現問題.
*/
public boolean addAll(Collection<? extends E> c) {
//轉為Object陣列
Object[] a = c.toArray();
//獲取陣列長度
int numNew = a.length;
//容量合法性判定
ensureCapacityInternal(size + numNew); // Increments modCount
//系統拷貝到elementData
System.arraycopy(a, 0, elementData, size, numNew);
//更改size
size += numNew;
return numNew != 0;
}
/**
* 將傳入集合c中的元素新增到ArrayList例項,新增開始的位置為指定的index.
* 工作過程:
* 首先,重置elementData的大小;
* 然後,向後移動elementData中下標範圍為:[index,index+numNew)的元素.
* 最後,將c中的元素拷貝到elementData中,elementData的拷貝位置從下標index開始.
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
/**
* 移除ArrayList例項中下標為[fromIndex,toIndex)的元素.
* 注意:刪除包括下標為fromIndex的元素,但不包括下標為toIndex的元素.
*/
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
//這裡又有和GC相關的操作啦
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
/**
* 這個方法唯一注意地方:就是對於給定的陣列下標index沒有判定為負數情況,為什麼沒有判定?
* answer: 因為這一方法總是在訪問陣列之前被呼叫,如果index為負數,則丟擲ArrayIndexOutOfBoundsException.
* 所以這裡沒有必要再判定一次index為負數的情況.那樣就很冗餘啦.
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//本方法用於:add方法或者addall方法,檢查插入位置index的合法性.
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 此方法功能:用於收集IndexOutOfBoundsException異常的細節資訊.
* 在錯誤處理程式碼的許多可能的重構中,這一構造方式對於伺服器和客戶端的虛擬機器表現都最好.
*/
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
/**
* 移除ArrayList例項中,和集合c中一樣的元素.
* 注意:如果集合c中不允許有null值,但是ArrayList例項中有,則會丟擲NullPointerException異常.
*/
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* 移除ArrayList中元素:
* 被移除元素滿足的條件是:在ArrayList例項中存在,而在集合c中不存在.
*/
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
/**
* 批量刪除方法
* 這個方法是上面兩個方法的輔助方法:
* 非常喜歡的一處程式碼:try裡面的for迴圈程式碼,很優雅的程式碼,完成了兩個方法的功能,如果是自己就估計寫成if判定了...
*/
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;//r記錄從左到右遍歷了list中幾個元素; w記錄了list中留下了幾個元素
boolean modified = false;
try {
for (; r < size; r++) //將符合條件的elementData中的元素左移.
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
/**
* 這裡的程式碼:是為了保持和AbstractCollection行為的相容性.
* 儘管在上述的c.contains裡面應該也會丟擲異常的.但是這裡還是處理了一下.
*
* 下面分析兩個if的功能:
* 第一個if:表面上看起來r!=size的條件永遠不會得到滿足,因為上述try裡面一直在r++呀.但是,你不要忘記,c.contains是可能丟擲兩類
* 異常的:ClassCastException和NullPointerException,這就會導致進入finally裡面,也就執行了這個if語句.
*
* 第二個if:又是GC啦,將elementData下標從w開始的值置為null,以便告訴JVM可以對elementData下標為[w,size-1]的位置進行回收啦.
*/
//try裡面的for迴圈未完成
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
//原elementData後面有刪除元素
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
/**
* 將ArrayList進行序列化:儲存一個ArrayList的狀態到輸出流中.
* 注意2點:
* 1.在將elementData中的元素寫入到輸出流之前,先將elementData的size寫入到輸出流中.
* 之前一直以為只向輸出流中寫入elementData中的資料呢.
* 2.defaultWriteObject方法功能:寫入類的no-static和no-transient欄位到輸出流中.
* 那麼問題來了,size應該是在這裡就被寫入到輸出流了,那麼下面為什麼還有s.writeInt(size)這一句,是否重複呢?
* answer: 原始碼給出的解釋是:作為clone()方法的相容行為,且被作為capacity欄位進行儲存.同時,我們可以在方法readObject中發現,
* 在通過方法defaultReadObject讀取了size以及其它隱藏屬性後,下一個讀取的int資料就是capacity.根據順序輸出和順序讀取的特點,我們知道
* 這個capacity就是我們在writeObject方法中s.writeInt(size).
*
* 3.defaultWriteObject方法再分析:,
* defaultReadObject()和defaultWriteObject()應該是readObject(ObjectInputStream o)和
* writeObject(ObjectOutputStream o)中的第一個方法呼叫。這些方法也有助於向後相容.如果將來你為這個類添加了一些非瞬態的欄位,
* 並且你試圖通過舊版本的類來反序列化它,那麼defaultReadObject()方法將會忽略新新增的欄位,類似地,如果你通過新的反序列化舊的
* 序列化物件版本,那麼新的非瞬態欄位將採取JVM中的預設值,比如int為0,boolean為false等.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
//根據stream中的描述資訊,構造一個ArrayList例項
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
//讀入size欄位值和其它隱藏屬性值
// Read in size, and any hidden stuff
s.defaultReadObject();
//讀入capacity欄位值
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
//基於size而非capacity進行記憶體分配
// 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();
}
}
}
/*--------3個不同型別迭代器的方法-------全都是fail-fast型別的.*/
//從指定下標index開始遍歷ArrayList例項,包含index.
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
* 這個方法是上述3個迭代器方法中第3個方法的輔助方法,也是它的核心演算法實現.
* 關於cursor和陣列元素下標的關係,這裡再次書寫一遍.例如elementData={1,2,3},則陣列下標分別為0,1,2,那麼加上類cursor後,該有的形式是:
* cursor0,0,cursor1,1,cursor2,2,cursor3.
* 也就是說cursor的值是在陣列元素之間的值,故cursor並不指向陣列元素.所以如果elementData長度為size,則cursor值有size+1個.
*
* 還要注意一點:Itr是個內部類.所以上述3個方法在呼叫時候,會使用到new欄位,來生成這個內部類,然後才能呼叫這個類裡面的方法.
*/
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
/**
* new這個類的時候,首先獲取當前ArrayList例項的modCount的值,是為了在進行迭代的時候,隨時檢查ArrayList例項的結構是否被更改,
* 一旦被更改,則儘可能早的丟擲異常.這點就是迭代器丟擲ConcurrentModificationException()一次的依據.
*/
int expectedModCount = modCount;
public boolean hasNext() { //cursor取值範圍:[0,size]
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
/**
* 每次next,都要宣告一個Object[] elementData,為什麼不在開始初始化類Itr時就宣告?
* 感覺好像應該是這樣的:就是這個引用基本和其它方法完全沒有關係,也就是elementData中的資料只和next()取值有關係,那麼每次呼叫
* next()方法時,就獲取一遍elementData的引用就行了.反正獲取引用也不會生成新的儲存空間,造成資源的浪費.這樣封裝起來的方法
* 看起來更舒服.
*/
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];//既返回了需要的值,又完成了對lastRet的賦值,完美!!!
}
/**
* 注意啦:這個方法是迭代器的remove方法,之前就說過,如果ArrayList例項在生成迭代器之後,如果再直接對ArrayList例項
* 改變結構的化,迭代器會丟擲異常ConcurrentModificationException.
* 但是通過迭代器對ArrayList例項進行結構對改變,則不會丟擲異常.這是怎麼辦到的呢?這就歸因於下面try裡面的程式碼啦.
* ok,分析一下try裡面的程式碼工作過程:
* 已經在每行程式碼後面寫好了……^_^
*
* 還有一點要注意的是:remove()方法刪除的就是ArrayList例項中的元素值,但這不影響對elementData的遍歷.因為每次刪除對都是
* 上一次已經遍歷過對值,這一點通過ArrayList.this.remove(lastRet)和cursor = lastRet可以知道.
* 所以remove()方法的實質:就是一邊遍歷,一遍刪除.刪除的都是上一次訪問的元素.最後elementData中元素全為null.
*/
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);//刪除ArrayList例項中下標為lastRet的元素
cursor = lastRet; //將cursor值更新到上一次訪問元素的下標值,也就是cursor=cursor-1.
lastRet = -1; //將上一次訪問元素的陣列下標置為-1,也就是初始化.
expectedModCount = modCount;//更新ArrayList例項結構被修改的次數,也就是expectedModCount=expectedModCount+1.
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(java.util.function.Consumer<? super E> consumer) {
//傳入函式介面不為空
Objects.requireNonNull(consumer);
//記錄size大小
final int size = ArrayList.this.size;
//記錄遊標
int i = cursor;
//如果遊標到達最後一個元素的後面
if (i >= size) {
return;
}
//記錄當前elementData陣列
final Object[] elementData = ArrayList.this.elementData;
//如果遊標>elementData陣列長度
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
//對遊標後的每一個元素都執行函式介面定義對accept操作.
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();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
/**
* 這個方法是上面3個迭代方法中前2個方法中被呼叫的方法.
* 這個方法是對AbstractList中的方法ListItr的優化版本.
* 這裡有2處要注意的地方:
* (1)set方法:不改變ArrayList的結構,只改變上一次訪問的ArrayList中的元素值.
* (2)add方法:改變ArrayList的結構,新增元素的位置是:上一次訪問的ArrayList中的元素的位置.
*
* 不管呼叫set方法還是add方法,ArrayList例項通過迭代器輸出的內容都不會改變.
*/
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);//保證了只更新上一次被訪問元素位置的值.
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;//保證了不訪問新增的元素值
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
/**
* 接下來是對subList()方法的介紹.讀懂了上面所有的程式碼,再看下面這些簡直太容易了.
* 但是但是但是,重要事情說三遍:
* 還是有要非常注意的地方,也是容易引起誤解的地方:
* subList()這個方法,功能是返回指定列表的部分檢視;
* 但是,它不是在java堆中新開闢的一個list物件,而是一個原列表檢視的一部分,因此不管這兩個列表誰發生了變化,都會體現在另一個列表上面.
* 這就和資料庫中的檢視一樣,對檢視的操作其實最終還是對生成檢視的基本表的操作,而對於基本表的操作導致資料發生改變時,也會體現在檢視上面.
*/
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;//設定了父類,從而為後面基於父類的操作做了準備工作
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
public int size() {
checkForComodification();
return this.size;
}
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);//為父類增加一個值
// this.modCount = parent.modCount; //更改父類的結構改變計數器
this.size++;
}
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);//為父類刪除一個值
//this.modCount = parent.modCount;//更改父類的結構改變計數器
this.size--;
return result;
}
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
// parent.removeRange(parentOffset + fromIndex,
// parentOffset + toIndex);
// this.modCount = parent.modCount;//更改父類的結構改變計數器
this.size -= toIndex - fromIndex;
}
public boolean addAll(Collection<? extends E> c) {
return addAll(this.size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
parent.addAll(parentOffset + index, c);//呼叫父類方法,為父類elementData增加新元素.
//this.modCount = parent.modCount;//更改父類的結構改變計數器
this.size += cSize;
return true;
}
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>() {
int cursor = index;
int lastRet = -1;
int expectedModCount = ArrayList.this.modCount;
public boolean hasNext() {
return cursor != SubList.this.size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= SubList.this.size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[offset + (lastRet = i)];
}
public boolean hasPrevious() {
return cursor != 0;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[offset + (lastRet = i)];
}
//jdk8新增
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = SubList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[offset + (i++)]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
SubList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(offset + lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
SubList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}
};
}
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, offset, fromIndex, toIndex);
}
private void rangeCheck(int index) {
if (index < 0 || index >= this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void rangeCheckForAdd(int index) {
if (index < 0 || index > this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+this.size;
}
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
}
//和上面forEachRemaining基本一樣
@Override
public void forEach(Consumer<? super E> action) {
//傳入函式介面不為空
Objects.requireNonNull(action);
//獲取當前list結構更改次數
final int expectedModCount = modCount;
//獲取當前陣列elementData
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
//獲取當前ArrayList中元素個數
final int size = this.size;
//對每一個元素執行函式介面定義的操作
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
//結構改變異常檢查
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**建立一個延遲繫結,快速失效的迭代器Spliterator
* @since 1.8
*/
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
//基於索引分成兩個延遲初始化分割器
//ArrayList分割器,實現了分割器介面
static final class ArrayListSpliterator<E> implements Spliterator<E> {
/**
* 如果ArrayList不可更改,or在結構不可變,我們可以用Arrays.spliterator來實現它們的分隔器.
* 相反,我們在遍歷過程中檢測到的干擾與實際情況一樣多,不會犧牲太多效能
* 我們主要依靠modcounts,但這並不能保證併發衝突,而且有時線上程衝突方面過於保守,但為了在實踐中檢測到足夠的問題這樣做是值得的.
*
* 為了做到這一點,
* (1)延遲初始化fence,expectedModCount,直到我們需要提交到我們正在檢查的狀態的最後一點;從而提升準確度.(這
* 並不適用於SubList,它在建立spliterator時,並未使用延遲載入策略)
*
* (2)我們只是在forEach方法的最後進行了併發異常檢查.當使用forEach時,我們在action後檢測異常,而並非之前.
* 更深層次的CME觸發器適用於所有其它的違反假設的情況,例如:null或者給定由於併發導致size特別小的elementData,.
* 這允許forEach在內部迴圈時,不再做更深層次的檢查,簡化了lambda表示式的action操作.
* 雖然這需要進行多次檢查,但請注意,在list.stream().forEach()的常見情況下,除了forEach本身之外,不會執行任何檢查或其他計算.
* 其它一些不常用的方法無法利用stream流提供的大多數優勢.
*/
private final ArrayList<E> list;
private int index; // current index, modified on advance/split當前索引,在前進,分割時發生值的更改.
private int fence; // -1 until used; then one past last index,未使用前是-1
private int expectedModCount; // initialized when fence set,當fence設定值時,為其初始化.
/** Create new spliterator covering the given range */
//根據給定的索引範圍建立新的分割器.
ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
int expectedModCount) {
this.list = list; // OK if null unless traversed
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
//第一次使用時,為fence初始化
private int getFence() {
int hi; //forEach方法中,一個指定出現的變數.
ArrayList<E> lst;
//如果hi=fence<0
if ((hi = fence) < 0) {
//如果list==null,則fence=0
if ((lst = list) == null)
hi = fence = 0;
//如果list!=null,則fence=list.size; 同時初始化expectedModCount
else {
expectedModCount = lst.modCount;
hi = fence = lst.size;
}
}
//返回fence
return hi;
}
//嘗試獲取後面一半的分割器.
public ArrayListSpliterator<E> trySplit() {
//hi=fence
//lo=index
//mid=(hi+lo)/2
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
//將分隔範圍一分為二;如果索引範圍太小,則不分割
//如果分割,則返回的分割器範圍是:[lo,mid).
return (lo >= mid) ? null : // divide range in half unless too small
new ArrayListSpliterator<E>(list, lo, index = mid,
expectedModCount);
}
/**
* 利用傳入函式介面,對elementData[index]處理;
* 處理成功:返回true;
* 處理失敗:返回false;
*/
public boolean tryAdvance(Consumer<? super E> action) {
//判定函式介面是否為null
if (action == null)
throw new NullPointerException();
//前置索引i ,後置分割索引hi
int hi = getFence(), i = index;
//前置索引必須<後置索引,否則返回false
if (i < hi) {
//前置索引+1
index = i + 1;
//獲取第一個元素,利用函式介面對其處理
@SuppressWarnings("unchecked") E e = (E)list.elementData[i];
action.accept(e);
//併發異常檢查
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
//利用傳入函式介面,對elementData中每一個元素做處理.
public void forEachRemaining(Consumer<? super E> action) {
int i, hi, mc; // hoist accesses and checks from loop
ArrayList<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {
if ((hi = fence) < 0) {
mc = lst.modCount;
hi = lst.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (; i < hi; ++i) {
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
//預估分割器size
public long estimateSize() {
return (long) (getFence() - index);
}
//ArrayList的SplIterator一些屬性值
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
//記錄刪除元素個數
int removeCount = 0;
//BitSet用每一位儲存一個true或者false值,通過set方法設定.
//刪除元素位置設為true.
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
//迴圈刪除
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
//併發異常檢查
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
//如果有元素刪除,則對elementData陣列左移
if (anyToRemove) {
//新size大小
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);//返回值為false的索引,因為這樣索引位置處的值沒有刪除
elementData[j] = elementData[i];
}
//null設定,為GC準備
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
//更新size值
this.size = newSize;
//併發異常檢查
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
//結構更改次數+1
modCount++;
}
//返回true:如果有元素刪除;反之false
return anyToRemove;
}
//利用傳入函式介面,更新elementData中每一個值
@Override
@SuppressWarnings("unchecked")
pu