中國 2 款疫苗進入新冠疫苗實施計劃疫苗庫
ArrayList
可調節列表容量大小的,基於陣列實現的容器,元素可以是 null
,該類大致上同 vector
相似,但沒有同步
其中 size(), isEmpty(), get(), set(), iterator(), listIterator()
等方法都是 O(1)的時間複雜度;add()
方法是 O(N)的時間複雜度
每個 ArrayList例項都有一個 容量(Capacity),即內部維護的陣列最多儲存元素的數量
當有元素加入到 ArrayList中的時候,它的容量能實現自動增長,可以使用 ensureCapacity()
方法保證容量足夠
我們不能保證通過 快速失敗機制來維護它的執行緒安全
一些初始化的資訊
/** * 一些初始化的資訊 */ // 序列化 ID private static final long serialVersionUID = 8683452581122892189L; // 初始容量 private static final int DEFAULT_CAPACITY = 10; // 用於空例項的共享陣列例項 private static final Object[] EMPTY_ELEMENTDATA = {}; // 用於預設大小的空例項的陣列,區別於 EMPTY_ELEMENTDATA[] 陣列 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 真實儲存 ArrayList元素的陣列緩衝區,其長度即為 ArrayList的容量,在第一次新增元素時就會擴充套件為 DEFAULT_CAPACITY transient Object[] elementData; // ArrayList的實際長度(儲存的元素的個數) private int size; // 當前 ArrayList被結構化修改的次數,結構化修改指的是 修改了列表長度或者以某種方式擾亂了列表使之在迭代時不安全的操作 // 該欄位由 Iterator或者 ListIterator或返回以上二者的方法使用,如果這個值發生了意料之外的改變,迭代器就會報併發修改異常。這提供了快速失敗解決方案 // 在其子類下使用 modCound是可選擇的(protected修飾了),如果子類希望提供快速失敗的迭代那麼它也可以在它的 add()等結構化修改方法中修改 modCount的值,單次的結構修改方法的呼叫最多隻能修改一次 modCount的值;但是如果不希望提供 快速失敗 的迭代器,子類可以忽略 modCount // 我之前一直以為 modCount定義在 迭代器中。。。,現在才知道定義在被迭代的物件內 protected transient int modCount; // 定義在 AbstractList中 // 列表最大能分配的量 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
構造方法
/** * 構造方法 */ // 空參構造 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } // 有參構造,指定集合的長度建立一個 ArrayList public ArrayList(int initialCapacity) { if (initialCapacity > 0) { // 依據輸入的大小,初始化 elementData[] 陣列(真實儲存元素的陣列) this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { // 使用預設的大小(也就是 10) this.elementData = EMPTY_ELEMENTDATA; } else { // 針對負數等隨便輸入的東西,報錯非法引數異常(非法的初始容量:xxx) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } // 有參構造,指定一個已有的集合建立 ArrayList(原集合內的元素會新增到 新建的ArrayList中),此時 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 { // 如果傳參的集合為空,當前 ArrayList的 elementData[]陣列就是空的 this.elementData = EMPTY_ELEMENTDATA; } }
針對 第三種構造方法的實驗:
- 傳入非空的集合,發現 ArrayList的容量等於傳入的集合內的元素個數(因為在 ArrayList內部 elementData是 private修飾的,且未提供 get方法,所以使用 反射暴力獲取其長度)
- 傳入空集合,ArrayList長度也為 0
-
有一個很有趣的點:
- 空參建立 ArrayList時,其 容量為 0,而一旦往裡面加入元素之後,容量就會變成 10(預設容量)
- 而針對傳入 集合作為引數的構造器建立的 ArrayList,當作為引數的集合為空時,ArrayList的容量為 0;但是向 ArrayList內新增元素之後,容量就是新增的元素的個數,不是 ArrayList預設的擴容規則
-
針對這個問題繼續進行實驗:
-
向兩個arraylist內分別新增 13個元素,發現 由集合傳參構造的 ArrayList的容量依舊等於傳參個數;空參構造的 ArrayList容量符合正常的擴容規則,即 原容量 * 1.5
-
常見方法
void trimToSize()
縮減列表的大小為當前已存的元素的個數
public void trimToSize() {
// 因為調整列表的容量是結構化修改,所以需要變更 modCount的值
modCount++;
// 如果當前的元素數 小於 最大容量
if (size < elementData.length) {
// 修改最大容量為當前元素的個數
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
// 使用這種寫法來實現陣列變小: elementData = Arrays.copyOf(elementData, size) 感覺好神奇啊。。
}
}
void ensureCapacity(int minCapacity)
增加當前列表的容量,至少是達到能夠容納所有元素的容量
public void ensureCapacity(int minCapacity) { // elementData和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA(就是一個空陣列)相等時 minExpand = 10,否則等於 0 // 意思就是說,只有剛開始,ArrayList還沒新增元素的時候,minExpand會是 10;新增過元素之後它就不乾淨了,minExpand = 0 了 int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It's already // supposed to be at default size. : DEFAULT_CAPACITY; // 至少你調整後的容器大小不能比元素數量小,對吧。。 if (minCapacity > minExpand) { // 定義放在下面 ensureExplicitCapacity(minCapacity); }}// 主要為了底層陣列擴容private void ensureExplicitCapacity(int minCapacity) { // 因為是結構性的修改,所以需要改變 modCount的值 modCount++; // overflow-conscious code // 判斷一下要不要擴容,如果改變之後的容量大於當前陣列最大容量,就要擴容了呀 if (minCapacity - elementData.length > 0) // 定義放在下面 grow(minCapacity);}// 底層 ArrayList擴容的方法,其實就是 elementData[]陣列擴大的方法private void grow(int minCapacity) { // overflow-conscious code // 獲取原來的容量 int oldCapacity = elementData.length; // 獲取常規的擴容大小 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果常規擴容之後還不夠大,索性就你要多少就給你多少吧 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,也就是說如果你要的容量很大,那麼就呼叫 另一個方法 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);}// 索求得太多了呀。。。private static int hugeCapacity(int minCapacity) { // 判斷 minCapacity是否小於0 ? if (minCapacity < 0) // overflow throw new OutOfMemoryError(); // MAX_VALUE = 0x7fffffff;MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 = 0x7fffffff - 8 ... return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}
int size()
返回當前列表的元素個數
注意:
- 這裡返回的是 元素的個數,而不是 列表的 容量
- 列表的 容量(Capacity)預設只能通過 elementData[]陣列的 length獲取,但是 elementData[]是 private修飾的,所以只能通過 反射(至少我只會反射獲取)
public int size() { // 返回當前 list的 elment的數量 // private int size; 儲存 list的實際元素個數 return size;}
// 反射獲取 elementData[]陣列的方法
public static int getArrayListCapacity(ArrayList<?> arrayList) {
Class<ArrayList> arrayListClass = ArrayList.class;
try {
Field field = arrayListClass.getDeclaredField("elementData");
field.setAccessible(true);
Object[] objects = (Object[]) field.get(arrayList);
return objects.length;
} catch (NoSuchFieldException e) {
e.printStackTrace();
return -1;
} catch (IllegalAccessException e) {
e.printStackTrace();
return -1;
}
}
boolean isEmpty()
判斷 列表是否為空
public boolean isEmpty() {
return size == 0;
}
boolean contains(Object o)
判斷列表中是否包含與指定值相同的元素,如果有,則返回 true;否則,返回 false
public boolean contains(Object o) { // 定位傳參值的位置,具體解釋在下一個 return indexOf(o) >= 0;}
int indexOf(Object o)
返回在列表中 與指定值相等的元素 第一次出現索引位置;如果 該值不存在,就返回 -1
public int indexOf(Object o) {
// 先判斷 o是否為 null
if(o == null) {
// 如果是 null,就遍歷檢索整個 elementData[]陣列,尋找 null值,返回陣列下標
for(int i = 0; i < size; i++) {
if(elementData[i] == null) {
return i;
}
}
} else {
// 否則,遍歷 elementData[]陣列,呼叫 equals()方法,判斷是否存在等值元素,返回陣列下標
for(int i = 0; i < size; i++) {
if(o.equals(elmentData[i])) {
return i;
}
}
}
// 如果都沒找到,就返回 -1
return -1;
}
int lastIndexOf(Object o)
類似 indexOf(Object o)
方法,只是返回的是 列表中元素最後出現的 陣列下標;如果列表中不存在,則返回 -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;}
Object clone()
返回一個列表例項的淺克隆拷貝,即聲明瞭另一個指向 堆中列表的引用
也就是說,如果你在 list1
中修改了某個元素,那麼在 list
中也會變化,因為二者指向同一個地址
public Object clone() { try { // 呼叫 Object類下的 clone()方法,強轉為一個 ArrayList ArrayList<?> v = (ArrayList<?>) super.clone(); // 拷貝新列表中的 elmentData[]陣列 v.elementData = Arrays.copyOf(elmentData, size); // 設定新陣列的 modCount次數為 0 v.modCount = 0; return v; } catch(CloneNotSupportedException e) { // this shouldn't happen, since we are cloneable throw new InternelError(e); }}
Debug後發現:
如果要實現深克隆,可以採用如下程式碼,將具體的內部的元素也克隆一份
@Test
public void testClone2() throws CloneNotSupportedException {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("張三", 23));
list.add(new Student("李四", 24));
ArrayList<Student> listCopy1 = (ArrayList<Student>) list.clone();
System.out.println(list == listCopy1);
System.out.println(list.equals(listCopy1));
ArrayList<Student> listCopy2 = new ArrayList<>();
for (Student student : list) {
listCopy2.add((Student) student.clone());
}
System.out.println(listCopy2);
}
Object[] toArray()
返回一個新的陣列,包含了列表中的所有的元素(依照列表內的順序排列)
返回的陣列和原來的列表無關,也就是說,對陣列內元素的修改不影響列表內元素的值
該方法充當了 集合類和 陣列類之間的橋樑
public Object[] toArray() { return Arrays.copyOf(elementData, size);}
<T> T[] toArray(T[] a)
類似上面的那個方法,也會把 列表轉換成 陣列
區別在於,這個陣列不再是 Object型別,而是指定的型別
- 如果列表內的元素符合傳入的引數,那麼就會以那個引數作為陣列元素型別
- 如果不符合,則會新建一個以執行型別為陣列型別的陣列
如果傳入的陣列的容量大於列表的容量,則在陣列中緊跟在列表最後一個元素之後的元素全部會被設定為 null
這樣有助於確定列表的容量
@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) { // 如果傳入的陣列比列表小,就會返回一個和列表一樣大的(注意 是 和列表一樣大的)陣列,也就是說會返回一個更大的陣列 if(a.length < size) { return (T[]) Arrays.copyOf(elementData, size, a.getClass()); } // 如果陣列的大小比列表大,就直接將 elementData[]中的值拷貝到 a[]即可,拷貝的長度為 size個,都是從 0開始拷貝 System.arrayCopy(elmentData, 0, a, 0, size); // 會有很神奇的一幕:如果陣列比列表大(陣列為10個單位,列表長3個單位),且陣列已經初始化了,則:arr[0] ~ arr[3]都是列表中的值,arr[4]會變成 null,arr[5] ~ arr[9] 是陣列的原來的值;估計是為了方便之後遍歷判斷吧 if(a.length > size) { a[size] = null; } return a;}
這部分原始碼比較難
-
泛型方法
-
定義泛型方法需要在返回值前加上泛型引數,如:
public <T> T[] method(T a) { ... }
-
描述 類時,使用
E
,如:ArrayList<E>
,它與泛型方法中的T
不同 -
父類物件能夠顯示的強制轉換為子類物件的前提是:該物件本質上是子類(或者孫子類等)物件
// o是一個子類物件 String的引用Object o = new String("abc");// 此時,強轉不會出錯String s = (String) o;// 但是如果如下定義,o不是子類物件的引用o = new Object();// 此時就會出錯s = (String) o;/*也就是說,至少要有多型那樣的樣子,父類引用指向子類物件之後,父類引用才能夠使用強制型別轉換變回子類引用*/
-
陣列也是物件,由 JVM提供定義和初始化。但是 Object[] 和 String[] 之間沒有繼承關係,但是 它們之間存在協變,使得陣列物件也能像子父類一樣轉換(Java中的強制型別轉換一般針對的只是單個物件,陣列的強轉一般都不好使)
Object[] o = new String[]{"abc"}; String[] s = (String[]) o; // 此時不會出錯 o = new Object[]{}; s = (String[]) o; // 此時會出錯
-
-
而之所以 將方法名定義為
public <T> T[] toArray(T[] a)
,是由於泛型會在編譯期被擦除,實際上 ArrayList中 elementData[]陣列就是一個 Object[]陣列transient Object[] elementData;
- 那為什麼不把方法名寫成
public E toArray()
呢?因為 泛型E
會被擦除,方法實際返回的仍然是 Object[] 而不是 String[],最後是由編譯器分析 列表物件聲明後追加強制轉換到結果上的,但是這樣的轉換是不能進行的! - 但是當加入了引數
T[] t
之後,會在插入時就把每個元素逐個強制轉換為 String,再加入到一個 String[]陣列中,然後返回 Object[],再由編譯器強轉為 String[],而這麼做是可行的!
- 那為什麼不把方法名寫成
-
Arrays.copyOf()
這個方法有很多過載形式-
public static <T> T[] copyOf(T[] original, int newLength)
:指定長度進行陣列拷貝,空的部分用 null來填充,這樣能保證定長;兩個陣列能保證內部元素相同 -
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)
:將原來 U型別的陣列,複製成指定長度的 T型別的陣列public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") // 先建立一個指定長度的空陣列,型別依據泛型 T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); // 呼叫底層的陣列內容複製方法 System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
-
public static byte[] copyOf(byte[] original, int newLength
、public static short[] copyOf(short[] original, int newLength)
、public static int[] copyOf(int[] original, int newLength)
、public static long[] copyOf(long[] original, int newLength)
、public static char[] copyOf(char[] original, int newLength)
、public static float[] copyOf(float[] original, int newLength)
、public static double[] copyOf(double[] original, int newLength)
、public static boolean[] copyOf(boolean[] original, int newLength)
:就直接指定了陣列拷貝的型別/** * 以 int型拷貝為例 */ public static int[] copyOf(int[] original, int newLength) { int[] copy = new int[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
-
-
System.arraycopy(elmentData, 0, a, 0, size)
是一個本地方法,方法原型為:public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
src
:源陣列srcPos
:源陣列中開始複製的下標值dest
:目標陣列destPos
:目標陣列中開始複製的下標值length
:複製多少個元素
E get(int index)
返回列表中 指定位置的元素
public E get(int index) { // 檢查索引是否正常,定義放在下面 rangeCheck(index); // 返回 elementData[]陣列中,該下標的元素 return elementData(index);}private void rangeCheck(int index) { // 如果指定的值大於陣列的最大下標,就丟擲異常 if(index >= size) { // 丟擲異常,outOfBoudnsMsg()方法定義放在下面 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }}private String outOfBoundsMsg(int index) { return "Index: " + index + ", Size: " + size;}
E set(int index, E element)
使用傳入的引數,替換指定位置的元素,並返回原來的值
public E set(int index, E element) {
// 同 get()方法一樣,先檢查 index是不是比 size小
rangeCheck(index);
// 獲取該位置上原來的值
E oldValue = elementData[index];
// 在 elementData[]陣列中替換該位置下的值
elementData[index] = element;
// 返回原來的值
return oldValue;
}
boolean add(E e)
在列表的末尾新增元素,事實上就是在 elementData[]陣列中新增下一個值
public boolean add(E e) {
// 判斷列表容量夠不夠,實際判斷 elementData[]數組裡還有沒有多餘的空閒空間
ensureCapacityInternal(size + 1); // increments modCount
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 確保容量夠用,calculateCapacity()計算擴容
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果列表是預設空參構造建立的,並且本次是第一次 add
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 那麼會直接把容量加到 10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 否則就正常 +1就行
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
// 結構化修改,增加該值,保證該集合是快速失敗的
modCount++;
// overflow-conscious code
// 判斷是否會溢位
if (minCapacity - elementData.length > 0)
// 如果會溢位,就需要擴容了(也就是說 原有的 size+1 > capacity了,裝不下了)
grow(minCapacity);
}
// grow()方法在之前的 ensureCapacity(int minCapacity)方法中解釋過了
void add(int index, E element)
在列表的指定位置插入指定的元素
public void add(int index, E element) {
// 專門為這個 add()方法寫了一個檢查範圍的函式,面子夠大的。。
rangeCheckForAdd(index);
// 確定容量
ensureCapacityInternal(size + 1); // increments modCount !!
// 複製陣列,將從插入位置之後開始的內容全部後移一位
System.arraycopy(elementData, index, elementData, index + 1, size - index);
// 設定值
elementData[index] = element;
size++;
}
private void rangeCheckForAdd(int index) {
// 如果要插入的索引 大於 當前最大索引 或者 小於0
if (index > size || index < 0)
// 丟擲異常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
對比 E set(int index, E element)
方法:
- set()方法單純的改變原來的值,add()方法會在原有的列表內部(注意是已有元素列表的裡面,index<size的情況下)新增元素
- 看起來 add()方法更適合用來進行 插排等排序操作
- 因為 add()方法會對向陣列內新增額外的元素所以,需要檢驗擴容 ensureCapacity()
E remove(int index)
移除列表中指定位置的元素,陣列中後續的元素都會左移一位,並且返回被移除的元素
public E remove(int index) {
// 檢查索引越界
rangeCheck(index);
// 結構化修改,變更 modCount的值
modCount++;
// 獲取舊的值
E oldValue = elementData(index);
// 計算需要移動的數字的個數
int numMoved = size - index - 1;
if(numMoved > 0) {
// 通過陣列拷貝的形式,將從待移除位置開始往後的所有元素前移一位
System.arraycopy(elementData, index+1, elementData, index, numMoved);
}
// 將最後一個元素置為 null(因為陣列拷貝的關係,在 elementData[size-1] 和 elementData[size-2]中存的都是最後一個值,重複了)
elementData[--size] = null; // clear to let GC do its work
// 返回舊的值
return oldValue;
}
boolean remove(Object o)
如果列表中存在和指定的值相同的元素,那麼就移除其中第一個出現的元素,返回 true;
如果不存在,啥都不幹,返回 false
public boolean remove(Object o) {
// 如果待移除的元素是 null
if(o == null) {
// 遍歷整個列表尋找 null值
for(int index = 0; index < size; index++) {
if(elementData[index] == null) {
// 通過索引移除,只是在 fastRemove()基本可以保證索引的合規
fastRemove(index);
return true;
}
}
// 如果元素不是 null
} else {
// 同樣遍歷列表,通過 equals()方法進行判斷
for(int index = 0; index < size; index++) {
if(o.equals(elementData[index])) {
// 快速移除
fastRemove(index);
return true;
}
}
}
// 沒找到呀,那就返回 false吧
return false;
}
// 對比 E remove(int index)方法,刪去了索引越界檢查和舊值返回
private void faseRemove(int index) {
// 修改 modCount值
modCount++;
// 計算需要移動的元素的個數
int numMoved = size - index - 1;
if(numMoved > 0) {
// 通過陣列拷貝進行移動
System.arraycopy(elementData, index+1, elementData, index, numMoved);
}
// 把最後一個重複的項設定為 null
elementData[--size] = null // clear to let GC do its work
}
void clear()
清除列表中的所有元素
public void clear() {
// 修改 modCount的值
modCount++;
// 遍歷整個陣列,把把每個引用都設定為 null
// clear to let GC do its work
for(int i = 0; i < size; i++) {
elementData[i] = null;
}
// 設定列表的長度為 0(列表為空)
size = 0;
}
.
boolean addAll(Collection<? extends E> C)
將作為引數傳入的集合內的元素 全部新增到 elementData[]陣列的尾部
public boolean addAll(Collection<? extends E> c) {
// 將傳入引數的集合轉為陣列
Object[] a = c.toArray();
// 獲取待插入的元素的個數
int numNew = a.length;
// 確保容量足夠,也就是說保證 elementData[]陣列能夠存下 size+newNum個元素
ensureCapacityInternal(size + newNum); // Increments modCount
// 通過陣列複製進行插入,將 a[]陣列從 0索引開始的 newNum個元素複製到 elementData[]陣列中從 size開始的位置
System.arraycopy(a, 0, elementData, size, newNum);
// 修改當前集合中元素的個數
size+=numNew;
// 返回集合是否插入成功,如果傳入的集合長度為0,則被認為插入失敗
return numNew != 0;
}
boolean addAll(int index, Collection<? extends E> c)
從當前列表的某個位置開始,將指定集合中的全部元素插入到當前列表中;該位置之後的所有元素全部右移
public boolean addAll(int index, Collection<? extends E> c) {
// 判斷這個索引位置是否合理
rangeCheckForAdd(index);
// 將傳入集合轉換為陣列
Object[] a = c.toArray();
// 獲取該陣列的長度
int numNew = a.length;
// 確保列表中元素位置夠用,即 capacity >= size+numNew
ensureCapacityInternal(size + numNew); // Increments modCount
// 計算指定索引之後的元素每個需要移動幾位
int numMoved = size - index;
if(numMoved > 0) {
// 通過陣列拷貝的方式,先將列表的 elementData[]陣列中的部分元素後移
System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
}
// 通過陣列拷貝的方式,將指定集合中的元素拷貝到當前 elementData[]陣列中,實現 addAll的效果
System.arraycopy(a, 0, elementData, index, numNew);
// 更新列表中元素的個數
size += numNew;
// 返回是否新增正常
return numNew != 0;
}
boolean removeAll(Collection<?> c)
移除當前列表中,同時存在與指定集合中的元素
public boolean removeAll(Collection<?> c) {
// 判斷集合 c引用非空
Obejcts.retuireNonNull(c);
// 呼叫批量刪除的方法,並且返回最終結果
return batchRemove(c, false);
}
public static <T> T requireNonNull(T obj) {
// 如果引數是 null,就丟擲異常
if(obj == null) {
throw new NullPointerException();
}
return obj;
}
private boolean batchRemove(Collection<?> c, boolean complement) {
// 建立一個區域性變數,作為 elementData[]的副本
final Object[] elementData = this.elementData;
// r用於遍歷整個陣列,w用於對具體的位置進行修改
int r = 0, w = 0;
// 修改標誌位
boolean modified = false;
try {
// 遍歷整個 elementData[]陣列
for(; r < size; r++) {
// 傳入集合中是否存在和 列表中值相同的元素,通過 complement決定是刪去相同的元素還是不同的元素
if(c.contains(elementData[r]) == complement) {
// 通過值的覆蓋實現,如果 complement = false
// 也就是說當滿足以上條件時,陣列內的元素不在集合中,將r處的值賦給w處,w和r都後移一位;
// 如果不滿足條件,即元素需要刪除,就讓 w不動,r後移
// 這樣可以實現:把需要移除的資料都替換掉,不需要移除的資料前移
elementData[w++] = elementData[r];
}
}
} finally {
// preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws
if(r != size) {
System.arraycopy(elementData, r, elementData, w, size - r);
w += size - r;
}
if(w != size) {
// clear to let GC do its work
// w之前的所有元素都是需要保留的元素,其之後的元素都可以重複的,就可以刪去了
for(int i = w; i < size; i++) {
elementData[i] = null;
}
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
舉個栗子
通過 Debug,我們能看到:
boolean retainAll(Collection<?> c)
在當前列表中,只保留在指定集合中出現過的元素
也就是說,保留交集
public boolean retainAll(Collection<?> c) {
// 保證 c不是 null
Objects.requireNonNull(c);
// 相比 removeAll(),這裡將 complement值設定為 true,表示在執行 batchRemove()方法時,只會將指定集合中出現的內容保留
return batchRemove(c, true);
}
void writeObject(java.io.ObjectOutputStream s)
ArrayList的序列化方法,儲存當前 list的例項狀態並以流的形式寫出去
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
// 先記錄當前的 modCount值,方便與之後進行比較,保證在寫出時內容沒有改變過
// 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(element[i]);
}
// 如果寫完之後發現,中途有其他執行緒修改的了列表中的內容,就丟擲併發修改異常
if(modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
void readObject(java.io.ObjectOutputStream s)
通過物件輸入流反序列化一個物件,即重新構建一個 list
private void readObject(java.io.ObjectOutputStream s) throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if(size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, 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();
}
}
}
ListIterator<E> listIterator(int index)
返回一個從指定位置開始的列表迭代器
public ListIterator<E> listIterator(int index) {
// 判斷索引值是否合理
if(index < 0 || index > size) {
throw new IndexOutOfBoundsException("Index: " + index);
}
return new ListItr(index);
}
ListIterator<E> listIterator()
返回一個包含全部元素的列表迭代器
public ListIterator<E> listIterator() {
// 直接返回全部元素的迭代器
return new ListItr(0);
}
Iterator<E> iterator()
返回一個包含全部元素的普通迭代器
public Iterator<E> iterator() {
return new Itr();
}
List<E> subList(int fromIndex, int toIndex)
根據給定的索引值,獲取當前列表的子列表(兩端元素都包含的,是閉區間)
但是,如果 fromIndex == toIndex,則會返回一個空集合
本質上是拷貝,子列表中對元素的非結構性修改會影響到父列表,反之同理
對子列表的操作和其他所有正常的 arraylist一樣
可以採取如下方法移除列表中的部分元素:list.subList(from, to).clear()
public List<E> subList(int fromIndex, int toIndex) { // 檢驗起始索引和結束索引是否合理 subListRangeCheck(fromIndex, toIndex, size); // 構造一個新的列表類,SubList是 AbstractList的子類,和 ArrayList是兄弟關係 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 + ")"); }}
※void forEach(Consumer<? super E> action)
可以使用 forEach配合 lambda表示式
public void forEach(Consumer<? super E> action) {
// 判斷非空引用
Objects.requireNonNull(action);
// 獲取修改值
final int expectedModCount = modCount;
final E[] elementData = (E[]) this.elementData;
// 遍歷每個元素,對每一個元素都呼叫 accpet()方法進行處理
for(int i = 0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if(modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
※Spliterator<E> spliterator()
建立一個 延遲繫結和 快速失敗的,確定大小的,且子迭代器也確定大小的,有序的一個可拆分迭代器,它也是用於迭代遍歷陣列的
但是有別於 Iterator,它用於並行操作,其資料來源可以是陣列、集合、IO通道或生成器函式
它可以單獨遍歷元素 tryAdvance()
,也可以批量遍歷元素 forEachRemainning()
,並且可以呼叫trySplit()
方法進行迭代器拆分,使之變成上一個 Spliterator
的一半大小
每個 Spliterator
例項中都定義了 int characteristics()
方法,返回當前例項的一個特徵集,這個集合包括:
public static final int ORDERED = 0x00000010
:表示迭代器會按照其原始順序迭代其中的元素public static final int DISTINCT = 0x00000001
:表示迭代器中的元素是沒有重複的public static final int SORTED = 0x00000004
:表示迭代器是按照某種方式排序後順序迭代其中的元素public static final int SIZED = 0x00000040
:表示迭代器的元素是個數是確定的public static final int NONNULL = 0x00000100
:表示迭代器中沒有 null元素public static final int IMMUTABLE = 0x00000400
:表示元素不可變public static final int CONCURRENT = 0x00001000
:表示迭代器可多執行緒操作public static final int SUBSIZED = 0x00004000
:表示子迭代器也是確定大小的
如果一個迭代器沒有報告 IMMUTABLE
或者 CONCURRENT
特徵時,其繫結資料來源後會檢查資料來源的結構化改動
後繫結的迭代器指其 繫結資料來源的行為 發生在其第一次遍歷、第一次拆分、第一次查詢大小時,而不是在迭代器建立後立刻繫結資料來源。
和其它迭代器一樣,繫結前對源的修改可以反應在迭代器遍歷過程中,而繫結後的對源的修改會導致ConcurrentModificationException異常。
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
boolean removeIf(Predicate<? super E> filter)
刪除滿足給定謂詞條件的所有元素,預設使用 iterator遍歷集合中的元素,並且使用 iterator.remove()刪除匹配的元素
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
// BitMap的底層維護了一個 long[]陣列
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
// 遍歷判斷哪些元素符合刪除條件,並且在 bitSet中記錄其索引值
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;
// 如果存在需要移除的元素
if (anyToRemove) {
// 計算移除後,剩下的元素個數
final int newSize = size - removeCount;
// 遍歷 bitSet,取出之前存入的需要刪除的元素的索引
for (int i = 0, j = 0; (i < size) && (j < newSize); i++, j++) {
// nextClearBit()返回下一個空的位置,也就是說,把要保留的全部左移
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
// 將多餘的元素設定為null
for (int k = newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
void replaceAll(UnaryOperator<E> operator)
通過 operator對每個元素進行加工,並將結果替換列表中原來的值
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
// 遍歷每一個元素,對其進行 apply()方法的操作,並且再賦值給自己
for(int i = 0; expectedModCount == modCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
if(modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
// 函式式介面,代表對某一個運算元的一次操作,引數和返回值都必須是同一型別
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
void sort(Comparator<? super E> c)
通過傳入的比較器對列表進行排序
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
// 底層呼叫陣列的排序方法
Arrays.sort((E[]) elementData, 0, c);
if(modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
// 根據給定的比較準則(Comparator),在指定的範圍內(範圍 前開後閉),對陣列內的元素進行排序
// 該排序是穩定的,相同值的元素不會發生交換
public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c) {
if(c == null) {
// 該方法內部也和下面的判斷類似,只不過是使用預設的比較器實現
sort(a, fromIndex, toIndex);
} else {
// 底層為 mergeSort()歸併排序,分而治之
rangeCheck(a.length, fromIndex, toIndex);
if(LegacyMergeSort.useRequested) {
// 底層為 binarySort()二叉排序
legacyMergeSort(a, fromIndex, toIndex, c);
} else {
TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
}
}
}
常用內部類
private Class Itr implements Iterator<E>
是對 AbstractList.Itr的一個優化版本
private class Itr implements Iterator<E> {
// 下一個元素的索引值
int cursor; // index of next element to return
// 上一個返回的元素的索引,如果沒有就是 -1
int lastRet = -1; // index of last element returned; -1 if no such
// 併發修改控制欄位
int expectedModCount = modCount;
// 空參構造
Itr() {
}
// 判斷是否還有下一個元素,正常索引值為 0 ~ size-1,如果 cursor == size,則表示已經全部迭代完了
public boolean hasNext() {
return cursor != size;
}
// 獲取下一個元素
@SuppressWarnings("unchecked")
public E next() {
// 判斷併發修改狀態
checkForComodification();
// 獲取下一個將要返回的索引值
int i = cursor;
// 如果下一個索引超出了最大位置,就丟擲異常
if (i >= size) {
throw new NoSuchElementException();
}
// 獲取當前列表中的 elementData[]陣列
Object[] elementData = ArrayList.this.elementData;
// 再檢索一遍索引位置,能走到這一步說明前面的 if()判斷已經通過了,如果這裡通不過則表明,其他執行緒對列表進行了修改,需要丟擲併發修改異常
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
// 修改指向下一個元素的指標
cursor = i + 1;
// 返回陣列中位於該索引位置的元素,同時將 lastRet設定為當前索引,並且由於 elementData[]為 Object陣列,所以進行強轉
return (E) elementData[lastRet = i];
}
// 移除當前元素
public void remove() {
// 如果還沒有呼叫過 next(),就直接 remove()了,就會丟擲異常,因為 lastRet預設是 -1
if (lastRet < 0)
throw new IllegalStateException();
// 檢查併發修改異常
checkForComodification();
try {
// 呼叫 list的 remove(int index)方法
ArrayList.this.remove(lastRet);
// 修改下一個索引地址的位置
cursor = lastRet;
lastRet = -1;
// 因為出現了列表元素的移除,在list中肯定會修改 modCount,這裡就需要更新 expectedModCount的值
expectedModCount = modCount;
// 思考為什麼會有可能出現下標越界異常?
// 如果出問題,肯定在 list.this.remove()方法裡,即 lastRet越界了
// 但是正常情況下,lastRet勤勤懇懇,我們之前的程式碼已經保證了 lastRet > 0了
// 在具體呼叫 remove()方法裡面,也會有 rangeCheck(index)
// 所以猜測肯定是其他執行緒乾的,那麼就需要丟擲 併發修改異常了呀
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// JDK8 流式程式設計
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
// Consumer過程必須定義,否則就丟擲異常
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
// 獲取 elementData[]陣列的引用
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
// 針對每個元素使用 accept()方法,具體的方法實現依據傳入的引數 consumer
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();
}
}
private class ListItr extends Itr implements ListIterator<E>
是 AbstractList.ListItr的一個優化版
private class ListItr extends Itr implements ListIterator<E> {
// 有參構造,設定開始迭代的位置
ListItr(int index) {
super();
cursor = index;
}
// 判斷當前位置之前有沒有元素了
public boolean hasPrevious() {
return cursor != 0;
}
// 獲取下一個索引的位置(注意:cursor始終指向下一個索引)
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 {
// 通過呼叫 list的 set()方法,直接對 list中的值進行修改
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 進行迭代時,在list中直接插入元素(注意:該位置之後的元素都會後移一位)
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
// 修改 expectModCount,有種監守自盜的感覺,忽然
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
private class SubList extends AbstractList<E> implements RandomAccess
ArrayList的切片類
private class SubList extends AbstractList<E> implements RandomAccess {
// 雖然AbstractList是抽象類,但這裡只是宣告,還沒有例項化,所以不會報錯
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
// 有參構造,一般 parent就是 ArrayList了,所以可以看作是多型
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();
// 實際上會在 arraylist中新增,並且是在指定索引的位置新增(arraylist中的索引值 = 切片類索引值 + 切片的起始值)
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
// 在切片類中移除元素
public E remove(int index) {
rangeCheck(index);
checkForComodification();
// 實際移除 arraylist中的元素
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);
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)];
}
@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()使用
public Spliterator<E> spliterator() {
checkForComodification();
return new ArrayListSpliterator<E>(ArrayList.this, offset,
offset + this.size, this.modCount);
}
}
※ static final class ArrayListSpliterator<E> implements Spliterator<E>
實力不夠講不好
static final class ArrayListSpliterator<E> implements Spliterator<E> {
/*
* If ArrayLists were immutable, or structurally immutable (no
* adds, removes, etc), we could implement their spliterators
* with Arrays.spliterator. Instead we detect as much
* interference during traversal as practical without
* sacrificing much performance. We rely primarily on
* modCounts. These are not guaranteed to detect concurrency
* violations, and are sometimes overly conservative about
* within-thread interference, but detect enough problems to
* be worthwhile in practice. To carry this out, we (1) lazily
* initialize fence and expectedModCount until the latest
* point that we need to commit to the state we are checking
* against; thus improving precision. (This doesn't apply to
* SubLists, that create spliterators with current non-lazy
* values). (2) We perform only a single
* ConcurrentModificationException check at the end of forEach
* (the most performance-sensitive method). When using forEach
* (as opposed to iterators), we can normally only detect
* interference after actions, not before. Further
* CME-triggering checks apply to all other possible
* violations of assumptions for example null or too-small
* elementData array given its size(), that could only have
* occurred due to interference. This allows the inner loop
* of forEach to run without any further checks, and
* simplifies lambda-resolution. While this does entail a
* number of checks, note that in the common case of
* list.stream().forEach(a), no checks or other computation
* occur anywhere other than inside forEach itself. The other
* less-often-used methods cannot take advantage of most of
* these streamlinings.
*/
private final ArrayList<E> list;
// 每次 split()或者 advance()時,這個值都會變
private int index; // current index, modified on advance/split
// 柵欄,即index可遍歷的上限
private int fence; // -1 until used; then one past last index
private int expectedModCount; // initialized when fence set
/**
* 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() { // initialize fence to size on first use
int hi; // (a specialized variant appears in method forEach)
ArrayList<E> lst;
// 如果 fence < 0的話,表示初始化
if ((hi = fence) < 0) {
// 如果集合為 null,那麼 fence也需要為 0
if ((lst = list) == null)
hi = fence = 0;
else {
expectedModCount = lst.modCount;
hi = fence = lst.size;
}
}
return hi;
}
// 進行子劃分
public ArrayListSpliterator<E> trySplit() {
// 從當前迭代器的中間元素進行劃分
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
// 返回從當前 index到 mid的子迭代器
return (lo >= mid) ? null : // divide range in half unless too small
new ArrayListSpliterator<E>(list, lo, index = mid,
expectedModCount);
}
// 針對當前索引指向的值,進行提前處理,Consumer是一個消費者
public boolean tryAdvance(Consumer<? super E> action) {
// 保證消費函式不可為 null
if (action == null)
throw new NullPointerException();
// 獲取 fence和 index
int hi = getFence(), i = index;
if (i < hi) {
index = i + 1;
// 獲取 i對應的元素
@SuppressWarnings("unchecked") E e = (E) list.elementData[i];
// 消費
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
// 為每個物件都進行一次操作
public void forEachRemaining(Consumer<? super E> action) {
// i為下標,hi為fence,mc為預期已改變的大小
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();
}
// 計算剩餘容量
public long estimateSize() {
// 最大容量 - 已經消費過了的資料
return (long) (getFence() - index);
}
// 返回當前迭代器底層列表的特徵值
public int characteristics() {
// 表示是 有序的、定長、子列表也定長的
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}