第一次遇到這個問題,有必要記錄一下。昨天在測試程序的時候出現這么個異常:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(Unknown Source) at java.util.HashMap$KeyIterator.next(Unknown Source) at com.rick.model.Relation.getBack(Relation.java:1500) at com.rick.model.Relation.getConMap(Relation.java:1447) at com.rick.model.Run.main(Run.java:74)
ConcurrentModificationException(並發修改異常)。編譯器提示異常拋出行位於set迭代器處,相比就是在遍歷set時出現了問題,附上我的代碼(邏輯項目,有點復雜,遞歸,求笛卡兒積):
//*下面一段代碼無法正常運行
1 public static HashSet<HashSet<Relation>> getBack(Relation temp_con){ 2 HashSet<HashSet<Relation>> return_set = new HashSet<HashSet<Relation>>();//這個return_set是返回用的,在最后用到 3 HashSet<HashSet<HashSet<Relation>>> set0 = new HashSet<HashSet<HashSet<Relation>>>();//checked 4 5 Iterator<HashSet<Relation>> it = temp_con.getBigSet().iterator(); 6 // for testing 7 /* while(it.hasNext()){ 8 HashSet<Relation> small_set = it.next(); 9 Iterator<Relation> ittt = small_set.iterator(); 10 System.out.println("this is a small set"); 11 while(ittt.hasNext()){ 12 Relation rett = ittt.next(); 13 System.out.println("hahahah" + rett.toString()); 14 15 } 16 }*/ 17 18 while(it.hasNext()){ 19 HashSet<Relation> small_set = it.next(); 20 //這是在算法描述中一直說到的新的set 21 HashSet<Relation> new_set = new HashSet<Relation>(); 22 // 我都不知道了 23 Iterator<Relation> it1 = small_set.iterator(); 24 while(it1.hasNext()){ 25 Relation rt = it1.next(); 26 if (rt.getType() == 0){ 27 new_set.add(rt); 28 } 29 else if (rt.getType() == 2 ) 30 set0.add(getBack(rt)); 31 } 32 33 34 if(set0.size() > 1){ 35 Iterator<HashSet<HashSet<Relation>>> it0 = set0.iterator(); 36 while(it0. hasNext()){ 37 38 HashSet<HashSet<Relation>> set1 = it0.next(); 39 HashSet<HashSet<Relation>> set_delete = null;//用來引用待會兒出現的set2,以便將set2從set0中remove 40 41 Iterator<HashSet<Relation>> its1 = set1.iterator(); 42 while(its1.hasNext()){ 43 HashSet<HashSet<Relation>> set123 = new HashSet<HashSet<Relation>>(); 44 HashSet<Relation> set11 = its1.next(); 45 Iterator<Relation> it11 = set11.iterator(); 46 if(it0.hasNext()){ 47 HashSet<HashSet<Relation>> set2 = it0.next(); 48 set_delete = set2; 49 Iterator<HashSet<Relation>> its2 = set2.iterator(); 50 while(its2.hasNext()){ 51 HashSet<Relation> temp_set = new HashSet<Relation>(new_set); 52 HashSet<Relation> set21 = its2.next(); 53 Iterator<Relation> it21 = set21.iterator(); 54 while(it11.hasNext()){ 55 Relation foradd = it11.next(); 56 if(!temp_set.contains(foradd)){ 57 temp_set.add(foradd); 58 } 59 } 60 it11 = set11.iterator();//每次得重新指向set11的開頭 61 while(it21.hasNext()){ 62 Relation foradd = it21.next(); 63 if(!temp_set.contains(foradd)){ 64 temp_set.add(foradd); 65 } 66 } 67 //於是添加新產生的集合 68 //HashSet<HashSet<Relation>> set123 = new HashSet<HashSet<Relation>>(); 69 set123.add(temp_set); 70 //要放入set0和后面一起求笛卡爾積 71 72 }// its2 的 while 結束了 73 }// if has next 74 set0.add(set123); 75 set0.remove(set1); 76 set0.remove(set_delete); 77 } 78 }// 79 }// END OF if 80 else if(set0.size() == 1){ 81 Iterator<HashSet<HashSet<Relation>>> it0 = set0.iterator(); 82 HashSet<HashSet<Relation>> set1 = it0.next(); 83 Iterator<HashSet<Relation>> its1 = set1.iterator(); 84 while(its1.hasNext()){ 85 HashSet<Relation> set11= its1.next(); 86 set11.addAll(new_set); 87 } 88 } 89 90 91 Iterator<HashSet<HashSet<Relation>>> it0 = set0.iterator(); 92 while(it0.hasNext()){ 93 Iterator<HashSet<Relation>> it9 = it0.next().iterator(); 94 while(it9.hasNext()){ 95 System.out.println("啦啦啦,我是賣報的小行家"); 96 return_set.add(it9.next()); 97 } 98 } 99 if(set0.size()<1){ 100 return_set.add(new_set); 101 } 102 System.out.println("return_set ' s size() is "+ return_set.size()); 103 104 }// END OF OUTER WHILE 105 106 107 return return_set; 108 109 }
編譯器提示的“at com.rick.model.Relation.getBack(Relation.java:1500)”就位於上一段代碼的第35行。
看了下Java 的API文檔,上網查了一下,大家討論的普遍是用迭代器遍歷set刪除元素時拋出“並發修改異常”,但我的問題好像要復雜不少,至少現在(2014.4.20上午)尚未找到解決辦法(可能會放棄set而使用List,但還是想繼續把問題搞明白點)。
來自網絡:
Iterator 是工作在一個獨立的線程中,並且擁有一個 mutex 鎖。 Iterator 被創建之后會建立一個指向原來對象的單鏈索引表,當原來的對象數量發生變化時,這個索引表的內容不會同步改變,所以當索引指針往后移動的時候就找不到要迭代的對象,所以按照 fail-fast 原則 Iterator 會馬上拋出 java.util.ConcurrentModificationException 異常。 所以 Iterator 在工作的時候是不允許被迭代的對象被改變的。但你可以使用 Iterator 本身的方法 remove() 來刪除對象, Iterator.remove() 方法會在刪除當前迭代對象的同時維護索引的一致性。
先來一下普通的用迭代器(Iterator)遍歷刪除元素的解決方案:
最理想的方法應該是使用Iterator類的remove()方法:
void remove()
從迭代器指向的 collection 中移除迭代器返回的最后一個元素(可選操作)。
所以在程序中運用it.remove()即可成功刪除元素
還有一種比較不錯的方法是用HashSet的clone()方法拷貝給另一個臨時的HashSet,然后用這個拷貝的HashSet的迭代器來完成原始HashSet的刪除工作。
Object clone()
返回此 HashSet 實例的淺表副本:並沒有復制這些元素本身。
添加元素的問題尚在研究之中。。。