本文已收錄到1.1K Star數(shù)開源學(xué)習(xí)指南——《大廠面試指北》,如果想要了解更多大廠面試相關(guān)的內(nèi)容及獲取《大廠面試指北》離線PDF版,請掃描下方二維碼碼關(guān)注公眾號“大廠面試”弓坞,謝謝大家了!
《大廠面試指北》最佳閱讀地址:
http://notfound9.github.io/interviewGuide/
《大廠面試指北》項(xiàng)目地址:
https://github.com/NotFound9/interviewGuide
獲取《大廠面試指北》離線PDF版车荔,請掃描下方二維碼關(guān)注公眾號“大廠面試”
《大廠面試指北》項(xiàng)目截圖:
簡介
我們在項(xiàng)目開發(fā)過程中渡冻,經(jīng)常會(huì)有需求需要?jiǎng)h除ArrayList中的某個(gè)元素,而使用不正確的刪除方式忧便,就有可能拋出異常族吻。或者在面試中珠增,會(huì)遇到面試官詢問遍歷時(shí)如何正常刪除元素呼奢。所以在本篇文章中,我們會(huì)對幾種刪除元素的方式進(jìn)行測試切平,并對原理進(jìn)行研究,希望可以幫助到大家辐董!
ArrayList遍歷時(shí)刪除元素的幾種姿勢
首先結(jié)論如下:
第1種方法 - 普通for循環(huán)正序刪除(結(jié)果:會(huì)漏掉元素判斷)
第2種方法 - 普通for循環(huán)倒序刪除(結(jié)果:正確刪除)
第3種方法 - for-each循環(huán)刪除(結(jié)果:拋出異常)
第4種方法 - Iterator遍歷悴品,使用ArrayList.remove()刪除元素(結(jié)果:拋出異常)
第5種方法 - Iterator遍歷,使用Iterator的remove刪除元素(結(jié)果:正確刪除)
下面讓我們來詳細(xì)探究一下原因吧简烘!
首先初始化一個(gè)數(shù)組arrayList苔严,假設(shè)我們要?jiǎng)h除等于3的元素。
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
removeWayOne(arrayList);
}
第1種方法 - 普通for循環(huán)正序刪除(結(jié)果:會(huì)漏掉元素判斷)
for (int i = 0; i < arrayList.size(); i++) {
if (arrayList.get(i) == 3) {//3是要?jiǎng)h除的元素
arrayList.remove(i);
//解決方案: 加一行代碼i = i - 1; 刪除元素后孤澎,下標(biāo)減1
}
System.out.println("當(dāng)前arrayList是"+arrayList.toString());
}
//原ArrayList是[1, 2, 3, 3, 4, 5]
//刪除后是[1, 2, 3, 4, 5]
輸出結(jié)果:
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
可以看到少刪除了一個(gè)3届氢,
原因在于調(diào)用remove刪除元素時(shí),remove方法調(diào)用System.arraycopy()方法將后面的元素移動(dòng)到前面的位置覆旭,也就是第二個(gè)3會(huì)移動(dòng)到數(shù)組下標(biāo)為2的位置退子,而在下一次循環(huán)時(shí)岖妄,i+1之后,i會(huì)為3寂祥,不會(huì)對數(shù)組下標(biāo)為2這個(gè)位置進(jìn)行判斷荐虐,所以這種寫法,在刪除元素時(shí)丸凭,被刪除元素a的后一個(gè)元素b會(huì)移動(dòng)a的位置福扬,而i已經(jīng)加1,會(huì)忽略對元素b的判斷惜犀,所以如果是連續(xù)的重復(fù)元素铛碑,會(huì)導(dǎo)致少刪除。
解決方案
可以在刪除元素后虽界,執(zhí)行i=i-1汽烦,使得下次循環(huán)時(shí)再次對該數(shù)組下標(biāo)進(jìn)行判斷。
第2種方法 - 普通for循環(huán)倒序刪除(結(jié)果:正確刪除)
for (int i = arrayList.size() -1 ; i>=0; i--) {
if (arrayList.get(i).equals(3)) {
arrayList.remove(i);
}
System.out.println("當(dāng)前arrayList是"+arrayList.toString());
}
輸出結(jié)果:
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
這種方法可以正確刪除元素浓恳,因?yàn)檎{(diào)用remove刪除元素時(shí)刹缝,remove方法調(diào)用System.arraycopy()將被刪除元素a后面的元素向前移動(dòng),而不會(huì)影響元素a之前的元素颈将,所以倒序遍歷可以正常刪除元素梢夯。
第3種方法 - for-each循環(huán)刪除(結(jié)果:拋出異常)
public static void removeWayThree(ArrayList<Integer> arrayList) {
for (Integer value : arrayList) {
if (value.equals(3)) {//3是要?jiǎng)h除的元素
arrayList.remove(value);
}
System.out.println("當(dāng)前arrayList是"+arrayList.toString());
}
}
輸出結(jié)果:
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.test.ArrayListTest1.removeWayThree(ArrayListTest1.java:50)
at com.test.ArrayListTest1.main(ArrayListTest1.java:24)
會(huì)拋出ConcurrentModificationException異常,主要在于for-each的底層實(shí)現(xiàn)是使用ArrayList.iterator的hasNext()方法和next()方法實(shí)現(xiàn)的晴圾,我們可以使用反編譯進(jìn)行驗(yàn)證颂砸,對包含上面的方法的類使用以下命令反編譯驗(yàn)證
javac ArrayTest.java//生成ArrayTest.class文件
javap -c ArrayListTest.class//對class文件反編譯
得到removeWayThree方法的反編譯代碼如下:
public static void removeWayThree(java.util.ArrayList<java.lang.Integer>);
Code:
0: aload_0
1: invokevirtual #12 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
4: astore_1
5: aload_1
6: invokeinterface #13, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 調(diào)用Iterator.hasNext()方法
11: ifeq 44
14: aload_1
15: invokeinterface #14, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;調(diào)用Iterator.next()方法
20: checkcast #9 // class java/lang/Integer
23: astore_2
24: aload_2
25: iconst_3
26: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
29: invokevirtual #10 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
32: ifeq 41
35: aload_0
36: aload_2
37: invokevirtual #15 // Method java/util/ArrayList.remove:(Ljava/lang/Object;)Z
40: pop
41: goto 5
44: return
可以很清楚得看到Iterator.hasNext()來判斷是否還有下一個(gè)元素,和Iterator.next()方法來獲取下一個(gè)元素死姚。而因?yàn)樵趧h除元素時(shí)人乓,remove()方法會(huì)調(diào)用fastRemove()方法,其中會(huì)對modCount+1都毒,代表對數(shù)組進(jìn)行了修改色罚,將修改次數(shù)+1。
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;
}
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
}
而當(dāng)刪除完元素后账劲,進(jìn)行下一次循環(huán)時(shí)戳护,會(huì)調(diào)用下面源碼中Itr.next()方法獲取下一個(gè)元素,會(huì)調(diào)用checkForComodification()方法對ArrayList進(jìn)行校驗(yàn)瀑焦,判斷在遍歷ArrayList是否已經(jīng)被修改腌且,由于之前對modCount+1,而expectedModCount還是初始化時(shí)ArrayList.Itr對象時(shí)賦的值榛瓮,所以會(huì)不相等铺董,然后拋出ConcurrentModificationException異常。
那么有什么辦法可以讓expectedModCount及時(shí)更新呢禀晓?
可以看到下面Itr的源碼中精续,在Itr.remove()方法中刪除元素后會(huì)對 expectedModCount更新坝锰,所以我們在使用刪除元素時(shí)使用Itr.remove()方法來刪除元素就可以保證expectedModCount的更新了,具體看第5種方法驻右。
private class Itr implements Iterator<E> {
int cursor; // 游標(biāo)
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;//期待的modCount值
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();//判斷expectedModCount與當(dāng)前的modCount是否一致
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;//更新expectedModCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
第4種方法 - Iterator遍歷什黑,使用ArrayList.remove()刪除元素(結(jié)果:拋出異常)
Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
if (value.equals(3)) {//3是要?jiǎng)h除的元素
arrayList.remove(value);
}
System.out.println("當(dāng)前arrayList是"+arrayList.toString());
}
輸出結(jié)果:
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.test.ArrayListTest1.removeWayFour(ArrayListTest1.java:61)
at com.test.ArrayListTest1.main(ArrayListTest1.java:25)
第3種方法在編譯后的代碼,其實(shí)是跟第4種是一樣的堪夭,所以第四種寫法也會(huì)拋出ConcurrentModificationException異常愕把。這種需要注意的是,每次調(diào)用iterator的next()方法森爽,會(huì)導(dǎo)致游標(biāo)向右移動(dòng)恨豁,從而達(dá)到遍歷的目的。所以在單次循環(huán)中不能多次調(diào)用next()方法爬迟,不然會(huì)導(dǎo)致每次循環(huán)時(shí)跳過一些元素橘蜜,我在一些博客里面看到了一些錯(cuò)誤的寫法,比如這一篇《在ArrayList的循環(huán)中刪除元素付呕,會(huì)不會(huì)出現(xiàn)問題计福?》文章中:
[圖片上傳失敗...(image-9f1ff1-1578232707248)]
先調(diào)用iterator.next()獲取元素,與elem進(jìn)行比較徽职,如果相等象颖,再調(diào)用list.remove(iterator.next());來移除元素,這個(gè)時(shí)候的iterator.next()其實(shí)已經(jīng)不是與elem相等的元素了姆钉,而是后一個(gè)元素了说订,我們可以寫個(gè)demo來測試一下
ArrayList<Integer> arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
arrayList.add(6);
arrayList.add(7);
Integer elem = 3;
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(arrayList);
if(iterator.next().equals(elem)) {
arrayList.remove(iterator.next());
}
}
輸出結(jié)果如下:
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 5, 6, 7]
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.test.ArrayListTest1.main(ArrayListTest1.java:29)
可以看到移除的元素其實(shí)不是3,而是3之后的元素潮瓶,因?yàn)檎{(diào)用了兩次next()方法陶冷,導(dǎo)致游標(biāo)多移動(dòng)了。所以應(yīng)該使用Integer value = iterator.next();將元素取出進(jìn)行判斷毯辅。
第5種方法 - Iterator遍歷埂伦,使用Iterator的remove刪除元素(結(jié)果:正確刪除)
Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
if (value.equals(3)) {//3是需要?jiǎng)h除的元素
iterator.remove();
}
}
輸出結(jié)果:
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
可以正確刪除元素。
跟第3種和第4種方法的區(qū)別在于是使用iterator.remove();來移除元素思恐,而在remove()方法中會(huì)對iterator的expectedModCount變量進(jìn)行更新沾谜,所以在下次循環(huán)調(diào)用iterator.next()方法時(shí),expectedModCount與modCount相等壁袄,不會(huì)拋出異常。
HashMap遍歷時(shí)刪除元素的幾種姿勢
首先結(jié)論如下:
第1種方法 - for-each遍歷HashMap.entrySet媚媒,使用HashMap.remove()刪除(結(jié)果:拋出異常)嗜逻。
第2種方法-for-each遍歷HashMap.keySet,使用HashMap.remove()刪除(結(jié)果:拋出異常)缭召。
第3種方法-使用HashMap.entrySet().iterator()遍歷刪除(結(jié)果:正確刪除)栈顷。
下面讓我們來詳細(xì)探究一下原因吧逆日!
HashMap的遍歷刪除方法與ArrayList的大同小異,只是api的調(diào)用方式不同萄凤。首先初始化一個(gè)HashMap室抽,我們要?jiǎng)h除key包含"3"字符串的鍵值對。
HashMap<String,Integer> hashMap = new HashMap<String,Integer>();
hashMap.put("key1",1);
hashMap.put("key2",2);
hashMap.put("key3",3);
hashMap.put("key4",4);
hashMap.put("key5",5);
hashMap.put("key6",6);
第1種方法 - for-each遍歷HashMap.entrySet靡努,使用HashMap.remove()刪除(結(jié)果:拋出異常)
for (Map.Entry<String,Integer> entry: hashMap.entrySet()) {
String key = entry.getKey();
if(key.contains("3")){
hashMap.remove(entry.getKey());
}
System.out.println("當(dāng)前HashMap是"+hashMap+" 當(dāng)前entry是"+entry);
}
輸出結(jié)果:
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key1=1
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key2=2
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key5=5
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key6=6
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 當(dāng)前entry是key3=3
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
at java.util.HashMap$EntryIterator.next(HashMap.java:1463)
at java.util.HashMap$EntryIterator.next(HashMap.java:1461)
at com.test.HashMapTest.removeWayOne(HashMapTest.java:29)
at com.test.HashMapTest.main(HashMapTest.java:22)
第2種方法-for-each遍歷HashMap.keySet坪圾,使用HashMap.remove()刪除(結(jié)果:拋出異常)
Set<String> keySet = hashMap.keySet();
for(String key : keySet){
if(key.contains("3")){
keySet.remove(key);
}
System.out.println("當(dāng)前HashMap是"+hashMap+" 當(dāng)前key是"+key);
}
輸出結(jié)果如下:
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key1
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key2
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key5
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key6
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 當(dāng)前key是key3
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
at java.util.HashMap$KeyIterator.next(HashMap.java:1453)
at com.test.HashMapTest.removeWayTwo(HashMapTest.java:40)
at com.test.HashMapTest.main(HashMapTest.java:23)
第3種方法-使用HashMap.entrySet().iterator()遍歷刪除(結(jié)果:正確刪除)
Iterator<Map.Entry<String, Integer>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
if(entry.getKey().contains("3")){
iterator.remove();
}
System.out.println("當(dāng)前HashMap是"+hashMap+" 當(dāng)前entry是"+entry);
}
輸出結(jié)果:
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key1=1
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key2=2
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key5=5
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key6=6
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key4=4
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 當(dāng)前entry是deletekey=3
第1種方法和第2種方法拋出ConcurrentModificationException異常與上面ArrayList錯(cuò)誤遍歷-刪除方法的原因一致,HashIterator也有一個(gè)expectedModCount惑朦,在遍歷時(shí)獲取下一個(gè)元素時(shí)兽泄,會(huì)調(diào)用next()方法,然后對
expectedModCount和modCount進(jìn)行判斷漾月,不一致就拋出ConcurrentModificationException異常病梢。
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
PS:ConcurrentModificationException是什么?
根據(jù)ConcurrentModificationException的文檔介紹梁肿,一些對象不允許并發(fā)修改蜓陌,當(dāng)這些修改行為被檢測到時(shí),就會(huì)拋出這個(gè)異常吩蔑。(例如一些集合不允許一個(gè)線程一邊遍歷時(shí)钮热,另一個(gè)線程去修改這個(gè)集合)。
一些集合(例如Collection, Vector, ArrayList哥纫,LinkedList, HashSet, Hashtable, TreeMap, AbstractList, Serialized Form)的Iterator實(shí)現(xiàn)中霉旗,如果提供這種并發(fā)修改異常檢測,那么這些Iterator可以稱為是"fail-fast Iterator"蛀骇,意思是快速失敗迭代器厌秒,就是檢測到并發(fā)修改時(shí),直接拋出異常擅憔,而不是繼續(xù)執(zhí)行鸵闪,等到獲取到一些錯(cuò)誤值時(shí)在拋出異常。
異常檢測主要是通過modCount和expectedModCount兩個(gè)變量來實(shí)現(xiàn)的暑诸,
modCount
集合被修改的次數(shù)蚌讼,一般是被集合(ArrayList之類的)持有,每次調(diào)用add()个榕,remove()方法會(huì)導(dǎo)致modCount+1expectedModCount
期待的modCount篡石,一般是被Iterator(ArrayList.iterator()方法返回的iterator對象)持有,一般在Iterator初始化時(shí)會(huì)賦初始值西采,在調(diào)用Iterator的remove()方法時(shí)會(huì)對expectedModCount進(jìn)行更新凰萨。(可以看看上面的ArrayList.Itr源碼)
然后在Iterator調(diào)用next()遍歷元素時(shí),會(huì)調(diào)用checkForComodification()方法比較modCount和expectedModCount,不一致就拋出ConcurrentModificationException胖眷。
單線程操作Iterator不當(dāng)時(shí)也會(huì)拋出ConcurrentModificationException異常武通。(上面的例子就是)
總結(jié)
因?yàn)锳rrayList和HashMap的Iterator都是上面所說的“fail-fast Iterator”,Iterator在獲取下一個(gè)元素珊搀,刪除元素時(shí)冶忱,都會(huì)比較expectedModCount和modCount,不一致就會(huì)拋出異常境析。
所以當(dāng)使用Iterator遍歷元素(for-each遍歷底層實(shí)現(xiàn)也是Iterator)時(shí)囚枪,需要?jiǎng)h除元素,一定需要使用 Iterator的remove()方法 來刪除簿晓,而不是直接調(diào)用ArrayList或HashMap自身的remove()方法,否則會(huì)導(dǎo)致Iterator中的expectedModCount沒有及時(shí)更新眶拉,之后獲取下一個(gè)元素或者刪除元素時(shí),expectedModCount和modCount不一致憔儿,然后拋出ConcurrentModificationException異常忆植。