若是你是一名Java開發人員,我可以肯定你確定知道ConcurrentModificationException,它是在使用迭代器遍歷集合對象時修改集合對象形成的(併發修改)異常。實際上,Java的集合框架是迭代器設計模式的一個很好的實現。 html
Java 1.5引入了java.util.concurrent包,其中Collection類的實現容許在運行過程當中修改集合對象。 java
ConcurrentHashMap是一個與HashMap很類似的類,可是它支持在運行時修改集合對象。 設計模式
讓咱們經過一個簡單的程序來幫助理解: 併發
ConcurrentHashMapExample.java app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
packagecom.journaldev.util;
importjava.util.HashMap;
importjava.util.Iterator;
importjava.util.Map;
importjava.util.concurrent.ConcurrentHashMap;
publicclassConcurrentHashMapExample {
publicstaticvoidmain(String[] args) {
//ConcurrentHashMap
Map<String,String> myMap =newConcurrentHashMap<String,String>();
myMap.put("1","1");
myMap.put("2","1");
myMap.put("3","1");
myMap.put("4","1");
myMap.put("5","1");
myMap.put("6","1");
System.out.println("ConcurrentHashMap before iterator: "+myMap);
Iterator<String> it = myMap.keySet().iterator();
while(it.hasNext()){
String key = it.next();
if(key.equals("3")) myMap.put(key+"new","new3");
}
System.out.println("ConcurrentHashMap after iterator: "+myMap);
//HashMap
myMap =newHashMap<String,String>();
myMap.put("1","1");
myMap.put("2","1");
myMap.put("3","1");
myMap.put("4","1");
myMap.put("5","1");
myMap.put("6","1");
System.out.println("HashMap before iterator: "+myMap);
Iterator<String> it1 = myMap.keySet().iterator();
while(it1.hasNext()){
String key = it1.next();
if(key.equals("3")) myMap.put(key+"new","new3");
}
System.out.println("HashMap after iterator: "+myMap);
}
}
|
當咱們試着運行上面的程序,輸出以下: 框架
1
2
3
4
5
6
7
|
ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1}
ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1}
HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1}
Exceptioninthread"main"java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:828)
at com.test.ConcurrentHashMapExample.main(ConcurrentHashMapExample.java:44)
|
查看輸出,很明顯ConcurrentHashMap能夠支持向map中添加新元素,而HashMap則拋出了ConcurrentModificationException。 spa
查看異常堆棧記錄,能夠發現是下面這條語句拋出異常: 翻譯
1
|
String key = it1.next();
|
這就意味着新的元素在HashMap中已經插入了,可是在迭代器執行時出現錯誤。事實上,集合對象的迭代器提供快速失敗(Fail-Fast)的機制,即修改集合對象結構或者元素數量都會使迭代器觸發這個異常。 設計
可是迭代器是怎麼知道HashMap被修改了呢,咱們能夠一次取出HashMap的全部Key而後進行遍歷。 htm
HashMap包含一個修改計數器,當你調用它的next()方法來獲取下一個元素時,迭代器將會用到這個計數器。
HashMap.java
1
2
3
4
5
6
7
|
/**
* HashMap結構的修改次數
* 結構修改是指:改變了HashMap中mapping的個數或者其中的內部結構(好比,從新計算hash值)
* 這個字段在經過Collection操做Hashmap時提供快速失敗(Fail-fast)功能。
* (參見 ConcurrentModificationException)。
*/
transientvolatileintmodCount;
|
如今爲了證實上面的觀點,咱們對原來的代碼作一點修改,使迭代器在插入新的元素後跳出循環。只要在調用put方法後增長一個break:
1
2
3
4
|
if(key.equals("3")){
myMap.put(key+"new","new3");
break;
}
|
再執行修改後的代碼,會獲得下面的輸出結果:
1
2
3
4
|
ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1}
ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1}
HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1}
HashMap after iterator: {3=1, 2=1, 1=1, 3new=new3, 6=1, 5=1, 4=1}
|
最後,若是咱們不添加新的元素而是修改已經存在的鍵值對會不會拋出異常呢?
修改原來的程序而且本身驗證一下:
1
2
|
//myMap.put(key+"new", "new3");
myMap.put(key,"new3");
|
若是你對於輸出結果感受困惑或者震驚,在下面評論。我會很樂意給出進一步解釋。
你有沒有注意到那些咱們在建立集合和迭代器時的尖括號,在Java中這叫作泛型,當涉及到編譯時的類型檢查和去除運行時的ClassCastException的時候會頗有幫助。點擊這裏能夠了解更多泛型教程。