使用迭代器时如何避免ConcurrentModificationException
在多線程以及單線程環(huán)境下都可能出現(xiàn)這種情況。
讓我們通過以下示例探索這種情況:
import java.util.*;public class IteratorExample {public static void main(String args[]){List<String> myList = new ArrayList<String>();myList.add("1");myList.add("2");myList.add("3");myList.add("4");myList.add("5");Iterator<String> it = myList.iterator();while(it.hasNext()){String value = it.next();System.out.println("List Value:"+value);if(value.equals("3")) myList.remove(value);}Map<String,String> myMap = new HashMap<String,String>();myMap.put("1", "1");myMap.put("2", "2");myMap.put("3", "3");Iterator<String> it1 = myMap.keySet().iterator();while(it1.hasNext()){String key = it1.next();System.out.println("Map Value:"+myMap.get(key));if(key.equals("2")){myMap.put("1","4");//myMap.put("4", "4");}}} }輸出為:
List Value:1 List Value:2 List Value:3 Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)at java.util.AbstractList$Itr.next(AbstractList.java:343)at com.journaldev.java.IteratorExample.main(IteratorExample.java:27)從輸出堆棧跟蹤中可以明顯看出,當(dāng)我們調(diào)用迭代器next()函數(shù)時(shí),異常即將到來。 如果您想知道Iterator如何檢查修改,則它的實(shí)現(xiàn)存在于AbstractList類中,其中定義了一個(gè)int變量modCount,該變量提供了更改列表大小的次數(shù)。 該值在每個(gè)next()調(diào)用中使用,以檢查功能checkForComodification()中是否有任何修改。
現(xiàn)在,注釋列表部分并再次運(yùn)行程序。
輸出將是:
Map Value:3 Map Value:2 Map Value:4由于我們正在更新myMap中的現(xiàn)有鍵值,因此其大小沒有更改,并且沒有收到ConcurrentModificationException。 請(qǐng)注意,輸出結(jié)果可能在您的系統(tǒng)中有所不同,因?yàn)镠ashMap鍵集的排序方式與列表不同。 如果您將在HashMap中添加新鍵值的語句取消注釋,則會(huì)導(dǎo)致ConcurrentModificationException。
要在多線程環(huán)境中避免ConcurrentModificationException:
1.您可以將列表轉(zhuǎn)換為數(shù)組,然后在數(shù)組上進(jìn)行迭代。 這種方法適用于中小型列表,但是如果列表很大,則對(duì)性能的影響很大。
2.您可以通過將列表放在同步塊中來在鎖定時(shí)鎖定列表。 不建議使用此方法,因?yàn)樗鼘⑼V苟嗑€程的好處。
3.如果您使用的是JDK1.5或更高版本,則可以使用ConcurrentHashMap和CopyOnWriteArrayList類。 這是推薦的方法。
要在單線程環(huán)境中避免ConcurrentModificationException:
您可以使用迭代器remove()函數(shù)從基礎(chǔ)集合對(duì)象中刪除該對(duì)象。 但是在這種情況下,您可以從列表中刪除同一對(duì)象,而不能刪除任何其他對(duì)象。
讓我們使用并發(fā)集合類運(yùn)行示例:
package com.journaldev.java;import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList;public class ThreadSafeIteratorExample {public static void main(String[] args) {List<String> myList = new CopyOnWriteArrayList<String>();myList.add("1");myList.add("2");myList.add("3");myList.add("4");myList.add("5");Iterator<String> it = myList.iterator();while(it.hasNext()){String value = it.next();System.out.println("List Value:"+value);if(value.equals("3")){myList.remove("4");myList.add("6");myList.add("7");}}System.out.println("List Size:"+myList.size());Map<String,String> myMap = new ConcurrentHashMap<String,String>();myMap.put("1", "1");myMap.put("2", "2");myMap.put("3", "3");Iterator<String> it1 = myMap.keySet().iterator();while(it1.hasNext()){String key = it1.next();System.out.println("Map Value:"+myMap.get(key));if(key.equals("1")){myMap.remove("3");myMap.put("4", "4");myMap.put("5", "5");}}System.out.println("Map Size:"+myMap.size());}}輸出為:
List Value:1 List Value:2 List Value:3 List Value:4 List Value:5 List Size:6 Map Value:1 Map Value:null Map Value:4 Map Value:2 Map Size:4從上面的示例可以清楚地看出:
1.可以修改Concurrent Collection類,避免ConcurrentModificationException 。
2.對(duì)于CopyOnWriteArrayList ,迭代器不適應(yīng)列表中的更改,并且可以處理原始列表。
3.對(duì)于ConcurrentHashMap ,其行為并不總是相同的。
條件:
if(key.equals("1")){myMap.remove("3");輸出為:
Map Value:1 Map Value:null Map Value:4 Map Value:2 Map Size:4它正在使用添加了鍵“ 4”的新對(duì)象。 但不是下一個(gè)添加的鍵為“ 5”的對(duì)象。
現(xiàn)在,如果我將條件更改為
if(key.equals("3")){myMap.remove("2");輸出為:
Map Value:1 Map Value:3 Map Value:null Map Size:4在這種情況下,它不考慮新添加的對(duì)象。
因此,如果您使用的是ConcurrentHashMap,請(qǐng)避免添加新對(duì)象,因?yàn)榭梢愿鶕?jù)鍵集對(duì)其進(jìn)行處理。 請(qǐng)注意,同一程序可以在您的系統(tǒng)中打印不同的值,因?yàn)镠ashMap鍵集沒有任何順序。
額外的澆頭:
for(int i = 0; i<myList.size(); i++){System.out.println(myList.get(i));if(myList.get(i).equals("3")){myList.remove(i);i--;myList.add("6");} }如果您正在單線程環(huán)境中工作,并且希望您的代碼處理列表中額外添加的對(duì)象,則可以使用以下代碼并避免使用迭代器。
請(qǐng)注意,由于要?jiǎng)h除同一對(duì)象,所以要減少計(jì)數(shù)器,如果必須刪除下一個(gè)或更遠(yuǎn)的對(duì)象,則不需要減少計(jì)數(shù)器。
自己嘗試。
參考:在JournalDev上 使用 JCG合作伙伴提供的迭代器時(shí)如何避免ConcurrentModificationException 。
相關(guān)文章:
- Java最佳實(shí)踐– Vector vs ArrayList vs HashSet
- Java最佳實(shí)踐–隊(duì)列之戰(zhàn)和鏈接的ConcurrentHashMap
- Java Fork / Join進(jìn)行并行編程
- ConcurrentLinkedHashMap v 1.0.1發(fā)布
- 阻塞隊(duì)列示例以執(zhí)行命令
- 限制URL連接的信號(hào)量示例
- 執(zhí)行命令的同步隊(duì)列示例
- 更一般的等待/通知機(jī)制的CountDownLatch示例
翻譯自: https://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的使用迭代器时如何避免ConcurrentModificationException的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Tomcat中的零停机部署(和回滚);
- 下一篇: 避免延迟的JPA集合