• 修改List报ConcurrentModificationException异常原因分析


    使用迭代器遍历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

     

     

     

    分类: java

  • 相关阅读:
    【转】全国各地做生意十年的心得,忍不住上来感慨一下,诚信才是根基!
    pbootcms常用标签
    wordpress常用标签
    表格
    dedecms忘记后台密码
    新手建站基础步骤
    zblog常用插件及操作步骤
    织梦建站如何添加视频文件
    #Centos7.4#Shell中判断语句if中-z至-d的意思
    #Centos7.4#Linux虚拟机KVM配置网卡【Requested operation is not valid: network 'br0' is not active】
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3273509.html
Copyright © 2020-2023  润新知