在用迭代器遍歷集合時,當集合的結構被修改,會拋出ConcurrentModificationException
異常。java
java.util
包下的集合類都是快速失敗的, 常見的的使用fail-fast
方式遍歷的容器有HashMap
和ArrayList
等。安全
1.單線程環境
集合在遍歷的過程當中,若是要對集合進行增刪操做,沒有調用迭代器的方法,而是用的集合自身的方法,則可能會產生fail-fast
(快速失敗)
2.多線程環境下
當一個線程在遍歷某個集合的過程當中,另外一個線程對集合的結構進行了修改,則可能產生fail-fast
(快速失敗)
多線程
具體效果咱們看以下代碼:spa
ArrayList arrayList = new ArrayList(); arrayList.add("a"); arrayList.add("c"); arrayList.add("d"); Iterator iterator = arrayList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); arrayList.add("e"); ① System.out.println("此時 arrayList 長度爲" + arrayList.size()); }
執行後的效果以下圖:
線程
當迭代器在遍歷時,不對arrayList
進行修改,也就是刪除①時。
3d
當迭代器在遍歷時,對arrayList
進行修改,也就保留①處代碼。就會拋出 ConcurrentModificationException
異常
code
modCount
變量。集合在被遍歷期間若是內容發生變化,就會改變modCount
的值。hashNext()/next()
遍歷下一個元素以前,都會檢測modCount
變量是否爲 expectedModCount
值,是的話就返回遍歷;不然拋出異常,終止遍歷。採用安全失敗的集合容器,在遍歷時不是直接在集合內容上訪問的,而是先複製原有集合內容,在拷貝的集合上進行遍歷,因此對原集合的修改並不會被迭代器檢測到對象
因爲迭代時是對原集合的拷貝進行遍歷,因此在遍歷過程當中對原集合所做的修改並不能被迭代器檢測到,因此不會拋 ConcurrentModificationException
異常。blog
java.util.concurrent
包下面的全部的類都是安全失敗的,如ConcurrentHashMap
, CopyOnWriteArrayList
等hash
具體效果咱們看以下代碼:
ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap(); concurrentHashMap.put("a", 1); concurrentHashMap.put("b", 2); concurrentHashMap.put("c", 3); Set set = concurrentHashMap.entrySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); concurrentHashMap.put("d", 4); ② } System.out.println("結束"); }
執行後的效果以下圖:
當迭代器在遍歷時,對concurrentHashMap
進行修改時,也就②。並未拋出任何異常
爲何在用迭代器遍歷時,修改集合不會拋異常?
因爲迭代時是對原集合的拷貝進行遍歷,內部都是保存了該集合對象的一個快照副本,而且沒有modCount
等數值作檢查。
fail-safe(安全失敗)容許在遍歷的過程當中對容器中的數據進行修改,而fail-fast(快速失敗)則不容許。