美文网首页
为什么ArrayList ForEach不能修改结构?

为什么ArrayList ForEach不能修改结构?

作者: 天不错啊 | 来源:发表于2021-09-12 14:00 被阅读0次

    前言

    我相信大家都知道ArrayList使用ForEach遍历时,不能修改结构。
    但是我一直知其然不知其所以然,有一次面试官问我这个问题,我发现从来都没考虑过原理。所以有了这篇博客。

    一、环境(Java)

    public class ArrayListForEachRemoveTest {
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList<>();
    
            for (int i = 0; i < 10; i++) {
                list.add(i);
            }
        
            // 第40行
            for (Integer integer : list) {
                list.remove(1);
            }
        }
    }
    

    当我准备编译的时候idea已经建议 不要在遍历中使用【list.remove】

    二、执行代码&处理结果

    不出意料的抛了一个异常。

    Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
        at java.util.ArrayList$Itr.next(ArrayList.java:861)
        at com.github.test.ArrayListForEachRemoveTest.main(ArrayListForEachRemoveTest.java:40)
    

    明明只是遍历了list,为什么报错在第40行循环上呢?
    下面让我们看看java文件编译后的结果。

    package com.github.test;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class ArrayListForEachRemoveTest {
        public ArrayListForEachRemoveTest() {
        }
    
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList();
    
            for(int i = 0; i < 10; ++i) {
                list.add(i);
            }
    
            Iterator var4 = list.iterator();
    
            while(var4.hasNext()) {
                Integer integer = (Integer)var4.next();
                list.remove(1);
            }
    
        }
    }
    

    原来ForEach语句只是一个语法糖,前端编译器只是将这个语法生成一个迭代器进行处理。
    异常结合class文件可知迭代器执行next()方法时抛出异常。

    private class Itr implements Iterator<E> {
            int expectedModCount = modCount;
            // ...
            public E next() {
                checkForComodification();
                // ...
            }
      
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
    
    }
    

    list有一个成员变量modCount,当创建迭代器时,直接通过外部类赋值expectedModCount变量。
    每次执行next()方法比较外部类和迭代器中的Count
    list调用add()remove()等方法时,会修改modCount的值。

    public E remove(int index) {
        rangeCheck(index);
    
        modCount++;
        E oldValue = elementData(index);
    
        int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
        elementData[--size] = null; // clear to let GC do its work
    
        return oldValue;
    }
    

    总结

    以前写代码从来没有注意到的细节,还是缺少了一些源码阅读的意识。

    相关文章

      网友评论

          本文标题:为什么ArrayList ForEach不能修改结构?

          本文链接:https://www.haomeiwen.com/subject/wyxtgltx.html