• Java 迭代器删除元素ConcurrentModificationException异常。


    Java是不支持容器类在使用迭代器迭代过程中,使用如 list.remove(obj)方法删除元素。否则会抛出ava.util.ConcurrentModificationException异常。应该使用iterator.remove()方法删除当前迭代到的元素。

    这是因为Java集合中有一种叫fail-fast的机制,即如果多个线程对同一个集合的内容进行操作时,则会产生fail-fast事件,即抛出异常。

    比如下面代码

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class Hello {
    
        public static void main(String[] args) {
            List<String> all=new ArrayList<String>();
            all.add("Hello");
            all.add("_");
            all.add("World!!");
            Iterator<String> iterator=all.iterator();
            while(iterator.hasNext())
            {
                String str=iterator.next();
                if("Hello".equals(str))
                {
                    all.remove(str);
    
                }
                else
                {
                    System.out.println( str+" ");
                }
            }
            System.out.println("
    删除"_"之后的集合当中的数据为:"+all);
        }
    }
    

    删除元素会抛出下面异常

    这里在迭代过程中改变了原来集合的存储内容,因此出发了fail-fast机制。

    fail-fast机制是这样设置的。以上面代码抛出的异常为例。查看ArrayList.类的源码,可以看到抛出异常的函数为

    这里的modCount文档是这样解释的

    The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
    

    大概意思就是list的结构被修改的次数。结构的修改包括改变list的大小,或者其他可能使迭代结果产生错误的干扰行为。

    expectedModCount注释为:

    The modCount value that the iterator believes that the backing List should have.  If this expectation is violated, the iterator has detected concurrent modification.
    

    因此若是在迭代过程中改变了List的结构,即使用了remove,或者等add方法则会导致expectedModCount的值与modCount的值不同,就会抛出异常。

    但是有一个奇特的现象,如果我们使用List.remove(obj)方法删除倒数第二个元素则不会超出异常。如

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class Hello {
    
        public static void main(String[] args) {
            List<String> all=new ArrayList<String>();
            all.add("Hello");
            all.add("_");
            all.add("World!!");
            Iterator<String> iterator=all.iterator();
            while(iterator.hasNext())
            {
                String str=iterator.next();
                if("_".equals(str))
                {
                    all.remove(str);
    
                }
                else
                {
                    System.out.println( str+" ");
                }
            }
            System.out.println("
    删除"_"之后的集合当中的数据为:"+all);
        }
    }
    

    注意这里删除的'_'为集合中的倒数第二个元素。同时注意代码有,若没有删除此元素,则输出此元素,即else里面的内容。结果为

    不是说,不能用List.remove(obj)的方式,只能用iterator.remove()的方式删除,这里为什么没有抛异常。原因是这样。

    看ArrayList的hasNext()方法与next()方法的源码

    size为当前集合的大小,cursor为下个元素的索引,初始化为0

    其中checkForComodification的源码为,两个变量的的解释上面也已经说了。

    我们的迭代器部分代码按下面步骤执行:

    all里面存储了“Hello”,"_","world!"三个元素

    1. iterator.hasNext(),
      1. cursor=0,size=3。cursor!=size,返回true,进入while内
    2. String str=iterator.next(),
      1. checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
      2. cursor=i+1,即cursor=1
      3. return elementData[lastRet=i],str='Hello'
    3. '_'.equals(str)为False,输出Hello
    4. iterator.hasNext(),
      1. cursor=1,size=3。cursor!=size,返回true,进入while内
    5. String str=iterator.next(),
      1. checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
      2. cursor=i+1,即cursor=2
      3. return elementData[lastRet=i],str='_'
    6. '_'.equals(str)为Ture,
      1. all.remove(str)
      2. modCount=1,因为改变了List的结构,因此modCount+1。
    7. iterator.hasNext(),
      1. cursor=2,size=2。cursor!=size,返回False,退出

    可以看到,在这里就已经推出while循环了。不会在执行checkForComodification()方法,判断List结构有没有改变。

    结论:因此只要对倒数第二个元素执行remove()方法,虽然会改变集合结构,但是因为也同时改变了集合size,使size与cursor的大小一样,就会直接循环,不会报异常。

  • 相关阅读:
    JSONObject对象和JSONArray对象的使用
    toString和getString的区别
    Pytorch 实验中非常有效的代码段
    TeXworks 高效使用指南
    linux 设置新用户
    ICML-21 待读的 Paper
    2019年12月19日记戴老师的讲座
    matplotlib 调整图片的 font size
    IJCAI 投稿 Type-3 字体
    matplotlib 中 figsize 和 dpi 之间的关系
  • 原文地址:https://www.cnblogs.com/lolybj/p/10982875.html
Copyright © 2020-2023  润新知