使用迭代器時如何避免ConcurrentModificationException

Java Collection類是快速失敗的,這意味著如果在使用迭代器遍歷某個線程的同時更改了Collection,則iterator.next()將拋出ConcurrentModificationException 。

在多線程以及單線程環境下都可能出現這種情況。

讓我們通過以下示例探索這種情況:

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)

從輸出堆棧跟蹤中可以明顯看出,當我們調用迭代器next()函數時,異常即將到來。 如果您想知道Iterator如何檢查修改,則它的實現存在于AbstractList類中,其中定義了一個int變量modCount,該變量提供了更改列表大小的次數。 該值在每個next()調用中使用,以檢查功能checkForComodification()中是否有任何修改。

現在,注釋列表部分并再次運行程序。

輸出將是:

Map Value:3
Map Value:2
Map Value:4

由于我們正在更新myMap中的現有鍵值,因此其大小沒有更改,并且沒有收到ConcurrentModificationException。 請注意,輸出結果可能在您的系統中有所不同,因為HashMap鍵集的排序方式與列表不同。 如果您將在HashMap中添加新鍵值的語句取消注釋,則會導致ConcurrentModificationException。

要在多線程環境中避免ConcurrentModificationException:

1.您可以將列表轉換為數組,然后在數組上進行迭代。 這種方法適用于中小型列表,但是如果列表很大,則對性能的影響很大。

2.您可以通過將列表放在同步塊中來在鎖定時鎖定列表。 不建議使用此方法,因為它將停止多線程的好處。

3.如果您使用的是JDK1.5或更高版本,則可以使用ConcurrentHashMap和CopyOnWriteArrayList類。 這是推薦的方法。

要在單線程環境中避免ConcurrentModificationException:

您可以使用迭代器remove()函數從基礎集合對象中刪除該對象。 但是在這種情況下,您可以從列表中刪除同一對象,而不能刪除任何其他對象。

讓我們使用并發集合類運行示例:

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.對于CopyOnWriteArrayList ,迭代器不適應列表中的更改,并且可以處理原始列表。

3.對于ConcurrentHashMap ,其行為并不總是相同的。

條件:

if(key.equals("1")){myMap.remove("3");

輸出為:

Map Value:1
Map Value:null
Map Value:4
Map Value:2
Map Size:4

它正在使用添加了鍵“ 4”的新對象。 但不是下一個添加的鍵為“ 5”的對象。

現在,如果我將條件更改為

if(key.equals("3")){myMap.remove("2");

輸出為:

Map Value:1
Map Value:3
Map Value:null
Map Size:4

在這種情況下,它不考慮新添加的對象。

因此,如果您使用的是ConcurrentHashMap,請避免添加新對象,因為可以根據鍵集對其進行處理。 請注意,同一程序可以在您的系統中打印不同的值,因為HashMap鍵集沒有任何順序。

額外的澆頭:

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");}
}

如果您正在單線程環境中工作,并且希望您的代碼處理列表中額外添加的對象,則可以使用以下代碼并避免使用迭代器。

請注意,由于要刪除同一對象,所以要減少計數器,如果必須刪除下一個或更遠的對象,則不需要減少計數器。

自己嘗試。

參考:在JournalDev上 使用 JCG合作伙伴提供的迭代器時如何避免ConcurrentModificationException

    相關文章:

    • Java最佳實踐– Vector vs ArrayList vs HashSet
    • Java最佳實踐–隊列之戰和鏈接的ConcurrentHashMap
    • Java Fork / Join進行并行編程
    • ConcurrentLinkedHashMap v 1.0.1發布
    相關片段:
    • 阻塞隊列示例以執行命令
    • 限制URL連接的信號量示例
    • 執行命令的同步隊列示例
    • 更一般的等待/通知機制的CountDownLatch示例

    翻譯自: https://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/news/374876.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/news/374876.shtml
    英文地址,請注明出處:http://en.pswp.cn/news/374876.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    Sublime Text 3實用快捷鍵大全

    下面是我通過網上教程和文本資料學習sublime Text3時收集的一些實用功能和常用快捷鍵&#xff0c;現在分享出來&#xff0c;如果還有其它的好用的功能可以在下面留言&#xff0c;以便互相學習和交流&#xff0c;謝謝&#xff01;。 選擇類 CtrlD 選中光標所占的文本&#xff0c…

    Tomcat中配置JNDI數據源

    準備工作&#xff1a; Tomcat版本&#xff1a;tomcat6.0以上 下例中均使用MySQL數據庫 將對應數據源的jar包和MySQL的驅動包拷貝至tomcat的lib文件夾下 一、全局數據源 1步驟一&#xff1a;配置 在tomcat下的conf/server.xml的GlobalNamingResources節點標簽中增加如下配置&…

    練習3-8 查詢水果價格 (15 分)

    練習3-8 查詢水果價格 (15 分) 給定四種水果&#xff0c;分別是蘋果&#xff08;apple&#xff09;、梨&#xff08;pear&#xff09;、桔子&#xff08;orange&#xff09;、葡萄&#xff08;grape&#xff09;&#xff0c;單價分別對應為3.00元/公斤、2.50元/公斤、4.10元/公…

    JavaFX 2.0 beta示例應用程序和思考

    我有一段時間回過頭來玩JavaFX&#xff0c;并且在使用該語言方面有好有壞的經驗。 隨著JavaFX 2.0 beta的發布&#xff0c;我想嘗試一下。 在這里&#xff0c;我開發了一個簡單的地址解析應用程序&#xff0c;該應用程序將使用Google地址編碼API來獲取地址并提供該位置的緯度-經…

    $Android自定義控件在不同狀態下的屬性

    在寫代碼的時候&#xff0c;有時候需要控件在不同狀態下顯示不同的外觀&#xff0c;比如在按鈕按下的時候要變顏色&#xff0c;EditText獲取焦點時候邊框要變顏色等。那么下面就來梳理一下這些是怎么實現的。 &#xff08;一&#xff09;按鈕按下時候變顏色 1、在項目的drawabl…

    解析DBR操作系統引導記錄數據

    理解文件系統。你必須要熟悉DBR&#xff0c;下面我們就來看看文件系統解析DBR數據。 Dos Boot Record(DBR)操作系統引導記錄是由操作系統的格式化程序建立的。在文件系統驅動操作不論什么一個磁盤卷時&#xff0c;這一部分的信息將被讀取并作為文件系統在這個磁盤卷上的參數被使…

    簡單冒泡排序

    將5個數字按從小到大排序。 #include <stdio.h> #include <stdlib.h> #include <math.h> int main() {int x[5] {0},temp 0;for(int i 0;i<5;i){scanf("%d",&x[i]);}//冒泡排序&#xff08;升序&#xff09;for(int j 0;j<4;j)//n個…

    YouTube Java API入門

    在本教程中&#xff0c;我將介紹Google的YouTube API &#xff0c;該API可讓您使用YouTube的功能來啟用應用程序。 YouTube是“殺手級”互聯網應用程序之一&#xff0c;其流量占互聯網總流量的很大一部分。 在開始之前&#xff0c;請確保您已閱讀《 API概述指南》 。 我們將主…

    mysql在mac上的坑

    默認端口3306&#xff1f; 正確答案&#xff1a;3307 轉載于:https://www.cnblogs.com/dudream/p/5375551.html

    ServletContext圖解

    servlet之間共享數據資源&#xff01; 轉載于:https://www.cnblogs.com/felixzh/p/4615902.html

    C語言怎么輸出百分號%

    規律&#xff1a;printf函數中&#xff0c;當出現多個%時&#xff0c;由左至右&#xff0c;每兩個%結合輸出一個% #include <stdio.h> #include <stdlib.h> #include <math.h> int main() {int c 52;printf("% \n %% \n %%% \n %%%% \n %%%%% \n %%%%…

    入侵Jasper以獲取JSP頁面的對象模型

    為了對我的JSP進行一些檢查和統計分析&#xff0c;我需要一個包含在其中的元素的類似于DOM的層次模型。 但是&#xff0c;解析JSP頁面并不是一件容易的事&#xff0c;最好留給它一個出色的工具-Tomcat&#xff0c;Jetty&#xff0c;GlassFish以及其他所有工具都可以使用Jasper …

    Linux自動化安裝cobbler

    1介紹 1.1 PXE PXE技術與RPL技術不同之處為RPL是靜態路由&#xff0c;PXE是動態路由。RPL是根據網卡上的ID號加上其他記錄組成的一個Frame&#xff08;幀&#xff09;向服務器發出請求。而服務器中已有這個ID數據&#xff0c;匹配成功則進行遠程啟動。PXE則是根據服務器端收到的…

    iOS9適配系列教程

    https://github.com/ChenYilong/iOS9AdaptationTips 轉載于:https://www.cnblogs.com/zsw-1993/p/4879118.html

    C語言形參

    形參和實參區別 形參出現在函數定義中&#xff0c;在整個函數體內都可以使用&#xff0c;離開該函數則不能使用。實參出現在主調函數中&#xff0c;進入被調函數后&#xff0c;實參變量也不能使用。 形參和實參的功能是作數據傳送。發生函數調用時&#xff0c;主調函數把實參…

    避免延遲的JPA集合

    Hibernate&#xff08;實際上是JPA&#xff09;具有集合映射&#xff1a; OneToMany&#xff0c; ManyToMany&#xff0c; ElementCollection。 所有這些默認情況下都是惰性的。 這意味著集合是List或Set接口的特定實現&#xff0c;其中包含對持久會話的引用&#xff0c;并且只…

    2016年,我的和自己談談

    2016年過去三分之一了&#xff0c;現在談規劃晚點但總比沒想法強。想了半天還是從這個方面著手吧&#xff1a; 一.升級改造自己的辦公學習環境&#xff1a; 給自己的電腦加內存&#xff0c;加SSD&#xff0c;再添置一個顯示器&#xff0c;換上心儀已久的cherry青軸鍵盤&#xf…

    C語言的四舍五入實現

    習題3-2 高速公路超速處罰 (15 分) 按照規定&#xff0c;在高速公路上行使的機動車&#xff0c;達到或超出本車道限速的10%則處200元罰款&#xff1b;若達到或超出50%&#xff0c;就要吊銷駕駛證。請編寫程序根據車速和限速自動判別對該機動車的處理。 輸入格式: 輸入在一行中…

    ACTGame項目

    項目地址&#xff1a;https://github.com/alonecat06/ACTGame游戲地址&#xff1a;http://pan.baidu.com/s/1hqD3IYw 項目是一個自制單機動作游戲demo&#xff0c;方向是手游&#xff0c;使用Unity5&#xff0c;5月中開工至今。 做這個項目&#xff0c;是為加深自己對Unity的理…

    Xuggler教程:幀捕獲和視頻創建

    注意&#xff1a;這是我們的“ Xuggler開發教程 ”系列的一部分。 到目前為止&#xff0c;在我們的Xuggler教程系列中&#xff0c;我們已經對視頻處理的Xuggler進行了介紹&#xff0c;并討論了轉碼和媒體修改 。 在本教程中&#xff0c;我們將看到如何解碼視頻和捕獲幀&#xf…