美文网首页
为什么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