java.util.ConcurrentModificationException異常分析

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。

Java在操作ArrayList、HashMap、TreeMap等容器類時,遇到了java.util.ConcurrentModificationException異常。以ArrayList為例,如下面的代碼片段:

?

[java]?view plain?copy
  1. import?java.util.ArrayList;??
  2. import?java.util.Iterator;??
  3. import?java.util.List;??
  4. import?java.util.concurrent.CopyOnWriteArrayList;??
  5. ??
  6. public?class?Test?{??
  7. ??
  8. ????public?static?void?main(String[]?args){??
  9. ????????List<String>?strList?=?new?ArrayList<String>();??
  10. ????????strList.add("string1");??
  11. ????????strList.add("string2");??
  12. ????????strList.add("string3");??
  13. ????????strList.add("string4");??
  14. ????????strList.add("string5");??
  15. ????????strList.add("string6");??
  16. ??????????
  17. ????????//?操作方式1:while(Iterator);報錯??
  18. ????????Iterator<String>?it?=?strList.iterator();??
  19. ????????while(it.hasNext())?{??
  20. ????????????String?s?=?it.next();??
  21. ????????????if("string2".equals(s))?{??
  22. ????????????????strList.remove(s);??
  23. ????????????}??
  24. ????????}??
  25. ??????????
  26. ????????//?解決方案1:使用Iterator的remove方法刪除元素??
  27. ????????//?操作方式1:while(Iterator):不報錯??
  28. //??????Iterator<String>?it?=?strList.iterator();??
  29. //??????while(it.hasNext())?{??
  30. //??????????String?s?=?it.next();??
  31. //??????????if("string2".equals(s))?{??
  32. //??????????????it.remove();??
  33. //??????????}??
  34. //??????}??
  35. ??????????
  36. ????????//?操作方式2:foreach(Iterator);報錯??
  37. //??????for(String?s?:?strList)?{??
  38. //??????????if("string2".equals(s))?{??
  39. //??????????????strList.remove(s);??
  40. //??????????}??
  41. //??????}??
  42. ??????????
  43. ????????//?解決方案2:不使用Iterator遍歷,注意索引的一致性??
  44. ????????//?操作方式3:for(非Iterator);不報錯;注意修改索引??
  45. //??????for(int?i=0;?i<strList.size();?i++)?{??
  46. //??????????String?s?=?strList.get(i);??
  47. //??????????if("string2".equals(s))?{??
  48. //??????????????strList.remove(s);??
  49. //??????????????strList.remove(i);??
  50. //??????????????i--;//?元素位置發生變化,修改i??
  51. //??????????}??
  52. //??????}??
  53. ??????????
  54. ????????//?解決方案3:新建一個臨時列表,暫存要刪除的元素,最后一起刪除??
  55. //??????List<String>?templist?=?new?ArrayList<String>();??
  56. //??????for?(String?s?:?strList)?{??
  57. //??????????if(s.equals("string2"))?{??
  58. //??????????????templist.add(s);??
  59. //??????????}??
  60. //??????}??
  61. //??????//?查看removeAll源碼,其使用Iterator進行遍歷??
  62. //??????strList.removeAll(templist);??
  63. ??????????
  64. ????????//?解決方案4:使用線程安全CopyOnWriteArrayList進行刪除操作??
  65. //??????List<String>?strList?=?new?CopyOnWriteArrayList<String>();??
  66. //??????strList.add("string1");??
  67. //??????strList.add("string2");??
  68. //??????strList.add("string3");??
  69. //??????strList.add("string4");??
  70. //??????strList.add("string5");??
  71. //??????strList.add("string6");??
  72. //??????Iterator<String>?it?=?strList.iterator();??
  73. //??????while?(it.hasNext())?{??
  74. //??????????String?s?=?it.next();??
  75. //???????????if?(s.equals("string2"))?{??
  76. //???????????????strList.remove(s);??
  77. //??????????}??
  78. //??????}??
  79. ??????????
  80. ????}?????
  81. }??

?

?

執行上述代碼后,報錯如下:

?

[plain]?view plain?copy
  1. Exception?in?thread?"main"?java.util.ConcurrentModificationException??
  2. ????at?java.util.ArrayList$Itr.checkForComodification(Unknown?Source)??
  3. ????at?java.util.ArrayList$Itr.next(Unknown?Source)??
  4. ????at?concurrentModificationException.Test.main(Test.java:21)??


在第21行報錯,即it.next(),迭代器在獲取下一個元素時報錯。找到java.util.ArrayList第830行,看到it.next()的源代碼,如下:

?

?

[java]?view plain?copy
  1. @SuppressWarnings("unchecked")??
  2. public?E?next()?{??
  3. ????checkForComodification();??
  4. ????int?i?=?cursor;??
  5. ????if?(i?>=?size)??
  6. ????????throw?new?NoSuchElementException();??
  7. ????Object[]?elementData?=?ArrayList.this.elementData;??
  8. ????if?(i?>=?elementData.length)??
  9. ????????throw?new?ConcurrentModificationException();??
  10. ????cursor?=?i?+?1;??
  11. ????return?(E)?elementData[lastRet?=?i];??
  12. }??


調用了checkForComodification()方法,代碼如下:

?

?

[java]?view plain?copy
  1. final?void?checkForComodification()?{??
  2. ????if?(modCount?!=?expectedModCount)??
  3. ????????throw?new?ConcurrentModificationException();??
  4. }??


即,比較modCount和expectedModCount兩個是否相等。modCount是ArrayList從AbstractList繼承來的屬性,查看modCount屬性的doc文檔,可知,modCount表示列表(list)被結構性修改(structurally modified)的次數。structurally modified是指造成列表中元素個數發生變化的操作,ArrayList中的add,addAll,remove, fastRemove,clear, removeRange, ?ensureCapacity, ?ensureCapacityInternal, ?ensureExplicitCapacity等方法都會使modCount加1,而batchRemove,removeAll,retainAll等方法則根據刪除的元素數增加modCount的值(removeAll和retainAll都是調用batchRemove實現,具體modCount的修改算法還需研究)。

?

第一個代碼片段中的操作方式1和操作方式2都是采用了迭代器的方式。當使用iterator方法得到Iterator對象(或者使用listIterator獲得ListItr對象),其實是返回了一個Iterator接口的實現類ArrayList$Itr(繼承自AbstractList$Itr),該類為ArrayList的一內部類,該類中有一個expectedModCount字段,當調用ArrayList$Itr的next方法時,會先檢查modCount的值是否等于expectedModCount的值(其實在調用next, remove, previous, set, add等方法時都會檢查),不相等時就會拋出java.util.ConcurrentModificationException異常。這種現象在Java?doc中稱作fail-fast。

為什么會拋出該異常呢?從代碼可以看到調用了6次add方法,這時modCount的值也就為6,當當使用iterator方法得到Iterator對象時把modCount的值賦給了expectedModCount,開始時expectedModCount與modCount是相等的,當迭代到第二個元素(index=1)“string2”時,因為if條件為true,于是又調用了remove方法,調用remove方法時modCount值又加1,此時modCount的值為7了,而expectedModCount的值并沒有改變,當再次調用ArrayList$Itr的next方法時檢測到modeCount與expectedModCount不相等了,于是拋出異常。

當把if語句寫成if(s.equals("string5"))時又沒有拋出該異常,這又是為什么呢?ArrayList$Itr中還有一個名為cursor的字段用來指向迭代時要操作的元素索引,初始值為0,每調用一次next方法該字段值加1,注意是先從集合中取出了元素再加1的。當判斷"string5"時,注意是倒數第二個元素,這些cursor的值為5,移除掉元素"string5"時,List的size為5,當調用ArrayList$Itr的hasNext方法判斷有無下一個元素時,判斷的依據為cursor的值與size是否相等,不相等則還有下一個元素,而此時兩者值剛好相等,也就沒有往下執行next方法了,也就沒有拋出異常,因此刪掉倒數第二個元素時不會拋異常的異常。
?

解決方案有四種,直接看第一段代碼即可。

?

參考鏈接:

http://blog.csdn.NET/xtayfjpk/article/details/8451178

《多線程情況下只能使用CopyOnWriteArrayList》http://www.2cto.com/kf/201403/286536.html

?

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

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

相關文章

redis基本數據類型之String

redis基本數據類型之String redis一共分為5中基本數據類型&#xff1a;String,Hash,List,Set,ZSet String String類型是包含很多種類型的特殊類型&#xff0c;并且是二進制安全的。比如序列化的對象進行儲存&#xff0c;比如一張圖片進行二進制儲存&#xff0c;比如一個簡單…

Laravel5.5之事件監聽、任務調度、隊列

一、事件監聽 流程&#xff1a; 1.1 創建event php artisan make:event UserLogin LoginController.php /*** The user has been authenticated.** param \Illuminate\Http\Request $request* param mixed $user* return mixed*/protected function authenticated(Request …

朱江洪功成身退 朱董配解體誰主格力(圖)

摘要&#xff1a;中國家電營銷委員會副理事長洪仕斌向時代周報記者表示&#xff1a;“朱江洪和董明珠已經完成了他們在格力發展前二十年的使命。“朱董配”解體之后&#xff0c;有人質疑格力“技術營銷”的格局必將被打破&#xff0c;難以延續&#xff0c;“董氏班底”與朱江洪…

一些dos下簡單命令

(1)切換盤符 d: 回車 (2)顯示某目錄下的所有文件或者文件夾(掌握) dir 回車 (3)創建文件夾 md 文件夾名稱 回車 (4)刪除文件夾 rd 文件夾名稱 回車 (5)進入目錄(掌握) 單級進入 cd 目錄名稱 多級進入 cd 目錄名稱1\目錄名稱2\... (6)回退目錄(掌握) 單級回退 cd.. …

ssh服務器拒絕了密碼 請再試一次 Xftp5連接失敗

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 我的情況都很簡單&#xff1a; 第一回主機 ip 不對&#xff0c; 第二次 是賬號、密碼都不對。 最后 IP、賬號、密碼都對了 就連上了。

后端DTO(數據傳輸對象)與DAO(數據庫數據源對象)解耦的好處

我們在后端的開發中經常會將DO對象傳到Service層直接作為DTO傳給前端&#xff0c;這樣做其實會有很多弊端。 &#xff08;一&#xff09;DO對象一般其成員域和數據庫字段是對應的&#xff0c;所以不能添加額外的字段&#xff0c;但是有時候端就是需要這個字段。反之前端要向后…

【刷算法】字符串的全排列

題目描述 輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。 分析 沒啥好分析的了&#xff0c;這個題不會&#xff0c;上網查的思路&#xff0c;大概就是&#xff1a; abc分化…

BZOJ.2741.[FOTILE模擬賽]L(分塊 可持久化Trie)

題目鏈接 首先記\(sum\)為前綴異或和&#xff0c;那么區間\(s[l,r]sum[l-1]^{\wedge}sum[r]\)。即一個區間異或和可以轉為求兩個數的異或和。 那么對\([l,r]\)的詢問即求\([l-1,r]\)中某兩個數異或的最大值。 區間中某一個數和已知的一個數異或的最大值可以用可持久化Trie \(O(…

傳騰訊人事大地震 馬化騰將重整公司架構

摘要&#xff1a;5月17日消息&#xff0c;傳騰訊董事長馬化騰將重新組織公司架構&#xff0c;為騰訊大換血。據悉&#xff0c;騰訊之所以選擇互動娛樂部門負責人接任這一重要崗位&#xff0c;也是因為互娛部門業績持續快速發展&#xff0c;成為了“騰訊帝國”發展的核心驅動力之…

阿里云對象存儲OSS與文件存儲NAS的區別

一、簡介 應用場景&#xff1a;選擇一款存儲產品&#xff0c;面向文檔數據的存取&#xff0c;不會涉及到數據處理。 產品選型主要從OSS和NAS中選擇一款&#xff0c;滿足文檔存儲的需求。 二、NAS優缺點 NAS 是一種采用直接與網絡介質相連的特殊設備實現數據存儲的機制。由于這些…

Thread.yield()

&#xff08;一&#xff09;java yield()方法注釋&#xff1a; /*** A hint to the scheduler that the current thread is willing to yield* its current use of a processor. The scheduler is free to ignore this* hint.** <p> Yield is a heuristic attempt to im…

WSDL 詳解

轉載自&#xff1a;http://kalogen.javaeye.com/blog/418958 WSDL (Web Services Description Language,Web服務描述語言)是一種XML Application&#xff0c;他將Web服務描述定義為一組服務訪問點&#xff0c;客戶端可以通過這些服務訪問點對包含面向文檔信息或面向過程調用的服…

MySQL數據類型char與varchar中數字代表的究竟是字節數還是字符數?

https://blog.csdn.net/zyz511919766/article/details/51682407 轉載于:https://www.cnblogs.com/zquan/p/9723082.html

傳蘋果新iPhone顯示屏4英寸 可視面積擴大30%

摘要&#xff1a;北京時間5月17日凌晨消息&#xff0c;據熟知內情的消息人士周三稱&#xff0c;蘋果計劃為其下一代iPhone使用更大的顯示屏&#xff0c;并已開始從韓國和日本供應商那里訂購新的顯示屏。業績人士指出&#xff0c;蘋果為下一代iPhone配備更大顯示屏的決定意味著&…

Ztree

引入css和js <link rel"stylesheet" href"/${appName}/commons/jslib/ztreeV3.5.15/css/zTreeStyle/zTreeStyle.css" type"text/css"></link> <script type"text/javascript" src"/${appName}/commons/jslib/ztre…

通過IDE生成和手動call調用webservice

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 通過IDE自動生成的代碼調用webservice服務 我們的IDE一般來說都是能夠通過各種各樣的工具來支持我們的開發使我們的開發變得更加的便捷。…

前端性能優化之Lazyload

前端性能優化之Lazyload (Mob前端-冬晨)[JavaScript|技術分享|懶加載] [TOC] Lazyload 簡介 前端工作中&#xff0c;界面和效果正在變得越來越狂拽炫酷&#xff0c;與此同時性能也是不得不提的問題。有些項目&#xff0c;頁面長&#xff0c;圖片多&#xff0c;內容豐富。像商城…

mysql查最大字符串

select MAX(comp_code0) from t_base_company字符串 0 把字符串轉成數字轉載于:https://www.cnblogs.com/feifeicui/p/9726401.html

中國聯通被指亂扣費 返還金額限制用

摘要&#xff1a;宋先生的聯通卡開通的是30G加100MB流量的套餐&#xff0c;宋先生上網認真核實了手機清單&#xff0c;發現近期上網流量從未超出。這回聯通客服的解釋是&#xff1a;“亂扣的費用已經在4月29日返還到你的卡里&#xff0c;這筆費用為‘隱藏扣費’&#xff0c;你是…

JAVA使用FTPClient類讀寫FTP

見&#xff1a;http://blog.csdn.net/kardelpeng/article/details/6588284 1.首先先導入相關jar包 2.創建一個連接FTP的工具類FTPUtil.Java [java] view plaincopy package com.metarnet.ftp.util; import java.io.IOException; import java.io.InputStream; import j…