• 为什么iterator,foreach遍历时不能进行remove操作?


    Exception in thread "main" java.util.ConcurrentModificationException 并发修改异常引发的思考!

    1 foreach循环删除元素

      ①list遍历删除元素时会报错,比如下面删除字符串"aa",也有遍历不报错的例子,看下面的例子

    public class TestMain {
        public static void main(String[] args) {
            ArrayList<String> array = new ArrayList<String>();
            array.add("cc");
            array.add("aa");
            array.add("bb");
            array.add("aa");   
            for (String str : array) {
                if("aa".equals(str)){
                    array.remove(str);
                }
            }
    
            System.out.println(array.size());
    
        }
    }
    
    console:   java.util.ConcurrentModificationException
    

      ②下面删除字符串"aa"不会报错

    public class TestMain {
        public static void main(String[] args) {
            ArrayList<String> array = new ArrayList<String>();
            array.add("cc");
            array.add("aa");
            array.add("bb");
            for (String str : array) {
                if("aa".equals(str)){
                    array.remove(str);
                }
            }
    
            System.out.println(array.size());
    
        }
    }
    
    console : 2
    
    

    提出问题:为什么上面都是遍历删除,第二个确没有报错呢?

    结论:其实原因很简单,因为第二个例子没有走iterator的next方法,删除了字符串"aa"之后,执行hasNext方法返回false直接退出遍历了,hasNext中就是判断cursor != size;此时的cursor是2,而size正好也是2,所以退出了遍历。

    而第一个例子删除字符串"aa"之后,cursor=2,size=3,所以hasNext方法返回的true,会执行next方法。

    :只要遍历中remove了,expectedModCount和modCount就不相等了。

    注2cursor 就类似游标,这里表示第几次,size就是元素个数

    同理:iterator的形式遍历同foreach。

    2 上面都是通过foreach的方式进行的,如果用普通for循环会怎么样

    public class TestMain {
        public static void main(String[] args) {
            ArrayList<String> array = new ArrayList<String>();
            array.add("cc");
            array.add("aa");
            array.add("bb");
            array.add("aa");
            for(int i = 0;i < array.size();i++){
                if("aa".equals(array.get(i))){
                  array.remove(i);
                }
            }
            System.out.println(array.size());
    
        }
    }
    
    console:  2 

      结论: 普通for循环可以正常删除,他是根据索引进行删除的,所以没有影响。

    根据报错信息可以看到是进入checkForComodification()方法的时候报错了,也就是说modCount != expectedModCount。具体的原因,是在于foreach方式遍历元素的时候,是生成iterator,然后使用iterator遍历。在生成iterator的时候,

    会保存一个expectedModCount参数,这个是生成iterator的时候List中修改元素的次数。如果你在遍历过程中删除元素,List中modCount就会变化,如果这个modCount和exceptedModCount不一致,就会抛出异常,这个是为了安全的考虑。

    看看list的remove源码:

    1 private void fastRemove(int index) {
    2         modCount++;
    3         int numMoved = size - index - 1;
    4         if (numMoved > 0)
    5             System.arraycopy(elementData, index+1, elementData, index,
    6                              numMoved);
    7         elementData[--size] = null; // clear to let GC do its work
    8     }

    remove操作导致modCount++

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
      public boolean hasNext() {
          return cursor != size;
      }
      public E next() {
                checkForComodification();
            try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
            } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
            }
        }
    
    
      final void checkForComodification() {
            if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        } 
    

     总结:  1 foreach遍历,iterator遍历都不能在遍历的过程中使用list.remove或list.add操作,会报并发修改异常。

        2 iterator遍历过程中如果需要删除可以使用iterator提供的remove()方法。

        3 遍历根据元素索引删除是可行的。

    以上属于个人心得,不对之处还望大佬指出,如果对你有帮助会更加激励我。  

     

  • 相关阅读:
    Day03_企业权限管理(SSM整合)
    Day02_企业权限管理(SSM整合)
    Day01_企业权限管理(SSM整合)
    MacOS下ElasticSearch学习(第一天)
    MacOS下Git安装及使用
    Hadoop 之 HDFS
    大数据采集、清洗、处理:使用MapReduce进行离线数据分析完整案例
    深入浅出爬虫之道: Python、Golang与GraphQuery的对比
    深入浅出分布式文件存储系统之 Ceph 的实现
    Pastebot—剪切板管理工具
  • 原文地址:https://www.cnblogs.com/liyong888/p/7799272.html
Copyright © 2020-2023  润新知