Iterable 接口是collection的父接口,他提供了Iterator方法來創建迭代器,意味着collection集合都有迭代能力
其他的注意事項不一一列舉,今天詳細解釋一下,在集合遍歷的過程中,不能給集合進行增刪操作,會報ConcurrentModificationException異常,以及爲什麼刪除倒數第二個元素的時候不會報錯的問題.
public class Demo01 {
public static void main(String[] args) {
Collection<String> test = new ArrayList<>();
test.add("AAAA");
test.add("BBBB");
test.add("CCCC");
test.add("DDDD");
Iterator<String> iterator = test.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
if("BBBB".equals(next)){
test.remove("BBBB");
}
}
}
}
在以上示例中
集合test有4個元素,我們在迭代的過程,remove第二個元素(注意這裏首先試一下刪除非倒數第二個元素的情況);
執行結果:
"C:\Program Files\Java\jdk-11.0.5\bin\java.exe" "-javaagent:C:\Program Files (x86)\IDEA\JetBrains\IntelliJ IDEA\lib\idea_rt.jar=5517:C:\Program Files (x86)\IDEA\JetBrains\IntelliJ IDEA\bin" -Dfile.encoding=UTF-8 -classpath D:\IDEAWorkSpace\day05\out\production\day05 com.cpic._01迭代器的練習.Demo01
AAAA
BBBB
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
at com.cpic._01迭代器的練習.Demo01.main(Demo01.java:26)
發現報錯了,查看源碼進行了解一下,
首先是hadNext()方法的源碼:
public boolean hasNext() {
return cursor != size;
}
就一句話,比較cursor和size是否相等,
其次查看Next()方法的源碼:
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];
}
首先是調用checkForComodification()方法,
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
比較modCount和expectedModCount是否相等(這邊請留意一下),其餘剩餘的就是判斷和拋異常了.
最後看一下remove(Object o)方法的源碼:
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
一開始是一些賦值判斷操作,也暫時不做說明,主要看一下最後調用的fastRemove(es,i)方法
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
重點來了,首先是modCount++,其次是一個System.arraycopy方法.
這裏額外講解一下System.arraycopy方法,
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):將數組中指定的數據拷貝到另一個數組中。數組的拷貝動作是系統級的,性能很高。 五個參數分邊是原數組,原數組索引起始位置,目標數組,目標數組索引起始位置,複製元素的個數,
重要方法的代碼講解完後,進行代碼流程講解.
第一步: 創建test集合並賦值,這裏進行了4次add操作,所以modCount爲4(這個參數是記錄操作次數的)並執行hasNext()
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;
所以expectedModCount的數值爲4
統計各個參數的數值
cursor = 0 (初始化的)
lastRet = -1 (初始化的)
size = 4 (集合長度)
ModCount = 4
expectedModCount = 4
第二步: cursor != size,進入next方法 :在checkForComodification方法中,ModCount == expectedModCount 所以繼續執行.
next中的兩個if都不滿足,所以參數賦值,return繼續進行,輸出結果AAAA
cursor = 1
lastRet = 0
size = 4 (集合長度)
ModCount = 4
expectedModCount = 4
第三步: 由於不滿足equals方法,所以程序執行hasNext方法, cursor != 4,進入next方法
第四步: 由於ModCount == expectedModCount 程序繼續執行,兩個if都不滿足,所以賦值return,輸出結果BBBB
cursor = 2
lastRet = 1
size = 4 (集合長度)
ModCount = 4
expectedModCount = 4
第五步: equals條件滿足,進入remove方法,結合源碼,前面是進行一系列賦值,判斷,循環,最後fastRemove方法參數,es爲集合,i = 1
首先modCount++;
然後System.arraycopy(es,2,es,1,2)
es{AAAA,BBBB,CCCC,DDDD},從索引爲2開始,截取2個元素,賦值到es中,索引從1開始,執行結果
es{AAAA,CCCC,DDDD},現在就是remove掉BBBB的結果
cursor = 2
lastRet = 1
size = 3
ModCount = 5
expectedModCount = 4
第六步: 執行hasNext方法,cursor != size,進入next方法
第七步: 在checkForComodification時, modCount != expectedModCount,所以報錯ConcurrentModificationException
===================================分割線===============================================
現在講解爲什麼remove倒數第二個數不會報錯.
代碼將BBBB改爲CCCC即可
public class Demo01 {
public static void main(String[] args) {
Collection<String> test = new ArrayList<>();
test.add("AAAA");
test.add("BBBB");
test.add("CCCC");
test.add("DDDD");
Iterator<String> iterator = test.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
if("CCCC".equals(next)){
test.remove("CCCC");
}
}
}
}
第一步: 創建test集合並賦值,並執行hasNext(),統計各個參數的數值
cursor = 0 (初始化的)
lastRet = -1 (初始化的)
size = 4 (集合長度)
ModCount = 4
expectedModCount = 4
第二步: cursor != size,進入next方法 :在checkForComodification方法中,ModCount == expectedModCount 所以繼續執行.
next中的兩個if都不滿足,所以參數賦值,return繼續進行,輸出結果AAAA
cursor = 1
lastRet = 0
size = 4 (集合長度)
ModCount = 4
expectedModCount = 4
第三步: 由於不滿足equals方法,所以程序執行hasNext方法, cursor != 4,進入next方法
第四步: 由於ModCount == expectedModCount 程序繼續執行,兩個if都不滿足,所以賦值return,輸出結果BBBB
cursor = 2
lastRet = 1
size = 4 (集合長度)
ModCount = 4
expectedModCount = 4
第五步, 由於不滿足equals方法,所以程序執行hasNext方法, cursor != 4,進入next方法
第六步: 由於ModCount == expectedModCount 程序繼續執行,兩個if都不滿足,所以賦值return,輸出結果CCCC
cursor = 3
lastRet = 2
size = 4
ModCount = 4
expectedModCount = 4
第七步: equals條件滿足,進入remove方法,結合源碼,前面是進行一系列賦值,判斷,循環,最後fastRemove方法參數,es爲集合,i = 2
首先modCount++;
然後System.arraycopy(es,3,es,2,1)
es{AAAA,BBBB,CCCC,DDDD},從索引爲3開始,截取1個元素,賦值到es中,索引從2開始,執行結果
es{AAAA,BBBB,DDDD},現在就是remove掉BBBB的結果
cursor = 3
lastRet = 2
size = 3
ModCount = 5
expectedModCount = 4
第八步: 執行hasNext方法,cursor == size,結束循環,沒有進入next中的checkForComodification給程序報錯的機會,所以沒有報錯
===============================================================================================
以上就是,對該問題的講解,由於書面表達流程,所以比較冗餘.如果有什麼疑問或者見解,歡迎大傢俬信.
推薦大家結合debug進行驗證,對結果更加鮮明.