• 增强for循环 java.util.ConcurrentModificationException


    Java中的Iterator功能比较简单,并且只能单向移动:

      (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

      (2) 使用next()获得序列中的下一个元素。

      (3) 使用hasNext()检查序列中是否还有元素。

      (4) 使用remove()将迭代器新返回的元素删除。

    迭代器应用:
               list l = new ArrayList();
               l.add("aa");
               l.add("bb");
               l.add("cc");
               for (Iterator iter = l.iterator(); iter.hasNext();) {
                          String str = (String)iter.next();
                          System.out.println(str);
               }
     /*迭代器用于while循环
               Iterator iter = l.iterator();
               while(iter.hasNext()){
                          String str = (String) iter.next();
                          System.out.println(str);
               }
     */

    Iterator升级

    在AbstractList中还有一个内部类Itr,Itr implements Iterator,Itr是一个List遍历的工具类,当然list.iterator()方法也是返回Itr对象,在Itr中有一个校验位属性expectedModCount,对于一个Itr对象,其初始时expectedModCount=modCount。
    【注】在AbstractList中,有一个属性modCount,这个属性是跟踪List中数据被修改的次数,任何对List的add/remove操作,都将导致modCount++。
    Iterator是List一个视图,其最终还是操作List的存储结构。在使用iterator遍历时,remove()操作,会导致modCount++,因为有expectedModCount=modCount,即在 iterator中remove数据,会带来expectedModCount与modCount值的同步。
    在Iterator遍历时,next(),remove()方法会校验expectedModCount与modCount值是否一致,如果不一致,就意味着这List数据在iterator外部被修改,此时iterator遍历将会造成 ConcurrentModificationException。
    AbstractList不仅支持普通的Iterator,还支持ListIterator(ArrayList,LinkedList均支持),ListIterator增加了遍历时双向游标能力(previous,next),增加了add方法。add方法和remove方法一样也做了expectedModCount和modCount一致性校验。
     
     
     
     

    在单线程操作的情况下,在DAO层查询到数据集合后,返回到service层做业务处理,要求:遍历数据集合,判断不符合条件的元素,做删除操作。

    在用foreach和 Iterator 都会发生java.util.ConcurrentModificationException。

    看一下JavaDoc对java.util.ConcurrentModificationException异常的描述:
    当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。

    查看源码后终于发现了原因是因为:

    迭代器的modCount和expectedModCount的值不一致。

    单线程中该异常出现的原因是:对一个集合遍历的同时,有对该集合进行了增删的操作。导致AbstarctList的modCount和expectedModCount的值不一致。

    而我们要做的就是将需要操作的元素放到中间元素中,并记录操作标志位。在遍历结束后进行增删操作。

    或自定义迭代器复写其中的相关操作,在操作结束后添加expectedModCount = modCount;

    多线程中更容易出现该异常,当你在一个线程中对一数据集合进行遍历,正赶上另外一个线程对该数据集合进行增删操作。

    解决方案:
    1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;

    2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。

    以下是Demo:推荐大家使用for循环进行遍历集合,在for循环中做增删操作。

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    
    public class Test {
         
         
        // 出现java.util.ConcurrentModificationException
        public List<String> m1(List<String> list) {
            for (String temp : list) {
                if ("3".equals(temp)) {
                    list.remove(temp);
                }
            }
            return list;
            
        }
        // 出现java.util.ConcurrentModificationException
        public List<String> m2(List<String> list) {
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String temp = iterator.next();
                if ("3".equals(temp)) {
                    list.remove(temp);
                }
                
            }
            return list;
            
        }
        //successful!
        public List<String> m3(List<String> list) {
            for (int i = 0; i < list.size(); i++) {
                if ("3".equals(list.get(i))) {
                    list.remove(i);
                }
            }
            return list;
            
        }
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            Test test = new Test();
            List<String> listTemp = test.m1(list);
            System.out.println(listTemp.toString());
        }
    }

    Iterator 在工作的时候是不允许被迭代的对象被改变的。

    但你可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

     

    正确的在遍历的同时删除元素的姿势:

    Iterator<Student> stuIter = students.iterator();    
    while (stuIter.hasNext()) {    
       Student student = stuIter.next();    
       if (student.getId() == 2)    
           stuIter.remove();//这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException    
    }

    参考:https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg%3D%3D&mid=2650121134&idx=1&sn=a34a1bd547f00e479e9f6dbde8848fe4&chksm=f36bbe8fc41c3799d1bb2c781f81f51e28651d2fb8eb5670a31caac5ba782b66416e5fdf1b1c&mpshare=1&scene=23&srcid=0414ouzb2yYypPWh2K0QVhtY%23rd

  • 相关阅读:
    html Table实现表头固定
    Asp.net ORA-12154: TNS: 无法解析指定的连接标识符
    VS加载项目时报错 尚未配置为Web项目XXXX指定的本地IIS
    Sqlserver 导出insert插入语句
    RRAS
    MVC实例应用模式
    MVC概述
    23种设计模式
    XXX系统质量属性
    大型网站架构读后感
  • 原文地址:https://www.cnblogs.com/acme6/p/9003454.html
Copyright © 2020-2023  润新知