欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

循环遍历删除元素的问题-ConcurrentModificationException

程序员文章站 2024-02-09 18:06:18
...

在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出bug。下面就是可能出现的一种删除方式。

	ArrayList<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(4);
        System.out.println(list);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer next = iterator.next();
            if (next==4)
                list.remove(next);
        }

这样就可能出现异常
[3, 4]
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayListItr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.checkForComodification(ArrayList.java:901) at java.util.ArrayListItr.next(ArrayList.java:851)
at Leetcode.LeetCode.main(LeetCode.java:68)
发生异常的主要原因就是
在list调用remove方法的时候

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

其中的fastRemove(index) 方法会修改一个modCount变量,这个变量的作用就是表示集合修改的次数(The number of times this list has been structurally modified.)

private void fastRemove(int index) {
        modCount++;
        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
    }

当fastRemove修改了modCount变量时,调用next方法

public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

其中checkForComodification方法首先就是判断modCount是不是和expectedModCount相等,不相等则抛出异常。

final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

其中expectedModCount在调用iterator方法的时候,就已经确定了
int expectedModCount = modCount;

注意一点,当删除元素的时候,进行了–size操作,这样在使用hasNext()方法判断集合还有没有元素的时候,使用的是

public boolean hasNext() {
            return cursor != size();
        }

因此,删除元素之后,即使后面已经没有元素了,可能size和cursor还是不相等,hasNext()返回的结果还为true。

修改代码

ArrayList<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(4);
        System.out.println(list);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer next = iterator.next();
            if (next==4)
                iterator.remove();
        }
        System.out.println(list);

输出结果
[3, 4]
[3]
为什么迭代器的删除方式不报异常呢?
主要是因为使用迭代器的remove方法,会把expectedModCount重新赋值。

public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }