使用迭代器遍历List的时候修改List报ConcurrentModificationException异常原因分析

 在使用Iterator来迭代遍历List的时候如果修改该List对象,则会报java.util.ConcurrentModificationException异常,下面看一个例子演示:

 1 package com.others;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.concurrent.CopyOnWriteArrayList;
7
8 public class ArrayListTest {
9
10 public static void main(String[] args) {
11 List<String> list = new ArrayList<String>();
12 //CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
13 list.add("a");
14 list.add("b");
15 list.add("c");
16 list.add("d");
17 list.add("e");
18 Iterator iterator = list.iterator();
19 while(iterator.hasNext()){
20 String str = (String) iterator.next();
21 if(str.equals("c")){
22 list.remove(str);
23 }else{
24 System.out.println(str);
25 }
26 }
27 }
28
29 }

  结果为:

a
b
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
at com.others.ArrayListTest.main(ArrayListTest.java:20)

  当调用list的iterator()方法的时候,返回的是一个Itr对象(实现了Iterator接口):

1 public Iterator<E> iterator() {
2 return new Itr();
3 }

  我们看一下Itr这个类:

 1 private class Itr implements Iterator<E> {
2 int cursor; // index of next element to return
3 int lastRet = -1; // index of last element returned; -1 if no such
4 int expectedModCount = modCount; //刚创建迭代对象的时候List的modCount
5
6 public boolean hasNext() {
7 return cursor != size;
8 }
9
10 @SuppressWarnings("unchecked")
11 public E next() {
12 checkForComodification(); //每调用一次next()函数都会调用checkForComodification方法判断一次
13 int i = cursor;
14 if (i >= size)
15 throw new NoSuchElementException();
16 Object[] elementData = ArrayList.this.elementData;
17 if (i >= elementData.length)
18 throw new ConcurrentModificationException();
19 cursor = i + 1;
20 return (E) elementData[lastRet = i];
21 }
22
23 public void remove() {
24 if (lastRet < 0)
25 throw new IllegalStateException();
26 checkForComodification();
27
28 try {
29 ArrayList.this.remove(lastRet);
30 cursor = lastRet;
31 lastRet = -1;
32 expectedModCount = modCount;
33 } catch (IndexOutOfBoundsException ex) {
34 throw new ConcurrentModificationException();
35 }
36 }
37 //此方法用来判断创建迭代对象的时候List的modCount与现在List的modCount是否一样,不一样的话就报ConcurrentModificationException异常
38 final void checkForComodification() {
39 if (modCount != expectedModCount)
40 throw new ConcurrentModificationException();
41 }
42 }

  List对象有一个成员变量modCount,它代表该List对象被修改的次数,每对List对象修改一次,modCount都会加1.

  Itr类里有一个成员变量expectedModCount,它的值为创建Itr对象的时候List的modCount值。用此变量来检验在迭代过程中List对象是否被修改了,如果被修改了则抛出java.util.ConcurrentModificationException异常。在每次调用Itr对象的next()方法的时候都会调用checkForComodification()方法进行一次检验,checkForComodification()方法中做的工作就是比较expectedModCount 和modCount的值是否相等,如果不相等,就认为还有其他对象正在对当前的List进行操作,那个就会抛出ConcurrentModificationException异常。

  我们再来分析一下上面那个例子,当例子程序执行到22行的时候,将list对象里面的“c"删除了,同时list对象的modCount值加1,但是Itr对象的expectedModCount没有变,他们肯定是不相等了。等再一次执行next()方法的时候调用了checkForComodification()方法,这时候就抛出异常了。

  我们再将上面那个例子稍微改动一下:将21行改为if(str.equals("d")){,即删除”d"这个元素。运行结果如下:

a

b

c

  这时却没有报异常了,但是“e"却没有输出来,这是为什么呢?原因很简单,我们看到Itr的hashNext()方法:

1 public boolean hasNext() {
2 return cursor != size;
3 }

  它是通过Itr的对象的cursor与List对象的size值来判断是否还有未迭代的对象,当遍历完“d"的时候cursor=4,删除”d"的时候,List对象的size就会减1,size首先为5,后来变为4,这时候cursor和size是相等的,hasNext()方法返回的是false,就认为遍历结束了,所以删除以后没有进去执行next()方法了,就没有抛出异常了,当然"e"也没有输出来。

  为了避免这种异常,我们可以使用CopyOnWriteArrayList来代替ArrayList,CopyOnWriteArrayList支持并发访问,所以同时进行迭代和修改是没有问题的。

我喜欢,驾驭着代码在风驰电掣中创造完美!我喜欢,操纵着代码在随必所欲中体验生活!我喜欢,书写着代码在时代浪潮中完成经典!每一段新的代码在我手中诞生对我来说就象观看刹那花开的感动!
欢迎分享与转载
0
 

修改List报ConcurrentModificationException异常原因分析的更多相关文章

  1. 使用迭代器遍历List的时候修改List报ConcurrentModificationException异常原因分析

    在使用Iterator来迭代遍历List的时候如果修改该List对象,则会报java.util.ConcurrentModificationException异常,下面看一个例子演示: package ...

  2. 使用迭代器遍历List的时候修改List报ConcurrentModificationException异常的解决办法

    为了避免这种异常,我们可以使用CopyOnWriteArrayList来代替ArrayList,CopyOnWriteArrayList支持并发访问,所以同时进行迭代和修改是没有问题的.

  3. 在多线程的情况下是由Iterator遍历修改集合对象,报ConcurrentModificationException()异常的根因分析

    遍历List时抛ConcurrentModificationException异常原理分析     http://www.blogjava.net/houlinyan/archive/2008/04/ ...

  4. Java ConcurrentModificationException异常原因和解决方法

    Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...

  5. Java并发编程:Java ConcurrentModificationException异常原因和解决方法

    Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...

  6. 【转】Java ConcurrentModificationException异常原因和解决方法

    原文网址:http://www.cnblogs.com/dolphin0520/p/3933551.html Java ConcurrentModificationException异常原因和解决方法 ...

  7. 9、Java ConcurrentModificationException异常原因和解决方法

    Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...

  8. (转)Java ConcurrentModificationException异常原因和解决方法

    转载自:http://www.cnblogs.com/dolphin0520/p/3933551.html 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会 ...

  9. java.util.ConcurrentModificationException异常原因及解决方法

    在java语言中,ArrayList是一个很常用的类,在编程中经常要对ArrayList进行删除操作,在使用remove方法对ArrayList进行删除操作时,报java.util.Concurren ...

随机推荐

  1. .net 控件开发第二天 怎么将 第一天写的代码 用到 .net中来

    前面第一天 我们看到的全是 js的代码,虽然不管是BS的框架是java 还是 php,复用性 还是特别高的,  但是 写起来比较费劲,怎么办,我们能不能 更 简单点呢? 当然可以,这个时候我们就要用到 ...

  2. RPC和RMI的区别(Difference Between RPC and RMI)

    RPC和RMI的区别(Difference Between RPC and RMI) RPC vs RMI RPC (Remote Procedure Call) and RMI (Remote Me ...

  3. SSAS系列——【08】多维数据(程序展现Cube)

    原文:SSAS系列--[08]多维数据(程序展现Cube) 1.引用DLL? 按照之前安装的MS SQLServer的步骤安装完成后,发现在新建的项目中“Add Reference”时居然找不到Mic ...

  4. 深入Objective-C的动态特性 Runtime

    Objective-C具有相当多的动态特性,基本的,也是经常被提到和用到的有动态类型(Dynamic typing),动态绑定(Dynamic binding)和动态加载(Dynamic loadin ...

  5. ASP.NET MVC3中Model验证

    原文:ASP.NET MVC3中Model验证 概述 上节我们学习了Model的数据在界面之间的传递,但是很多时候,我们在数据传递的时候为了确保数据的有效性,不得不给Model的相关属性做基本的数据验 ...

  6. C# 关闭子线程的方法

    将子线程设定为IsBackGroud = true,程序关闭则自动死亡. 暴力手段:System.Environment.Exit(0).关闭时直接x掉所有线程. 子线程自行结束:子线程中自己判断.可 ...

  7. 页面中引入js的几种方法

    通常大家最为熟悉的是一下两种方法: 在页面中直接写入<script type="text/javascript">js代码</script>. 在页面中引入 ...

  8. BitNami-Redmine安装和VisualSVN-Server配合使用

    原文:BitNami-Redmine安装和VisualSVN-Server配合使用 BitNami-Redmine安装和VisualSVN-Server配合使用 2011-05-04 10:48:22 ...

  9. 如何使用AdvancedInstaller在安装包中运行一个.bat文件

    原文:如何使用AdvancedInstaller在安装包中运行一个.bat文件 1,  首先要保证你的Files and Folders模块下的Application Folder文件夹下包含你要运行 ...

  10. Visual Studio 2013 的 Browser Link 功能

    Visual Studio 2013 的 Browser Link 功能 最近公司弄新项目需要用 MVC,就把 IDE 升级到了 Visual Studio 2013,在开发的时候发现有好多请求一个本 ...