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.mzph.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…