首页 > 其他 > 详细

ArrayList在for循环中remove所产生的问题

时间:2021-04-21 17:24:06      阅读:35      评论:0      收藏:0      [点我收藏+]

简介:

刚入职公司的时候,就听到面试官在面试过程中提问ArrayList在for循环中remove的问题,当时很庆幸自己没被问到,一年后又一次听到面试在问这个问题。发现自己还没有深入研究一下,所以就有了今天这篇文章。

代码如下:

import java.util.ArrayList;
import java.util.List;

public class ArrayListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");

        for (String str : list) {
            if ("aaa".equals(str)) {
                list.remove("aaa");
            }
        }
    }
}

以上代码的执行会导致以下异常:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at future.ArrayListTest.main(ArrayListTest.java:14)

今天通过一种通俗易懂的方式说明代码异常的原因!

1??list.add

通过查看 list.add 方法,发现有一个变量的值发生了改变,这个变量叫 modCount 。在上面demo中一共执行了四次 add 操作,所以 modCount 的值为4.

注:listadd()remove()clear() 都会改变 modCount 值。

技术分享图片

2??for(String str : list)

for(String str : list) 调用的是 ArrayList 的内部类 ItrItr是对 Iterator 的实现。在 Iterator 开始之前,会先执行 modCount != expectedModCount

此时 modCountexpectedModCount 的值均为 4 .

3??list.remove("aaa")

先看一下报错原因,这里是源码:

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

modcountexpectedModCount 的值不相等了。看到这里应该有人会想,如果每次执行 remove 后,将 expectedModCount = modcount 不就好了。其实是有的,只是我们调用的方法错了。

list.remove("aaa")调用的 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])) {
                //走的是这个remove
                fastRemove(index);
                return true;
            }
    }
    return false;
}

而使 modCount 的值改变的是其中的 fastRemove 方法。

fastRemove 源码如下:

/*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
private void fastRemove(int index) {
    //此处 modCount + 1
    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
}

通过查看上面的源码发现 list.remove(E e) 方法执行完成后只执行了 modcount++,并没有将值赋给 expectedModCount

而真正的 expectedModCount = modCount 执行的源码如下:

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();
    }
}

该方法位于内部类 Itr 中。

这也就是说为什么如果 list 在循环中有删除操作,最好用 Iterator 的方式来做。

结论

简单总结一下

  • list.remove() 没有对 expectedModCount 重新赋值
  • iterator.remove() 每次 remove 之后会对 expectedModCount 重新赋值

ArrayList在for循环中remove所产生的问题

原文:https://www.cnblogs.com/leizzige/p/14685275.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!