ArrayList在foreach循环的时候remove元素报错

示例代码:

public class ArrayListTest {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("hello1");
		list.add("hello2");
		list.add("hello3");
		list.add("hello4");
		list.add("hello5");
		for(String s : list){
			list.remove("hello2");
		}
	}
}

报错结果:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at com.collection.list.ArrayListTest.main(ArrayListTest.java:18)

再看这个问题之前大家先来了解一下foreach的本质是什么,
参考此博文:https://blog.csdn.net/wangjun5159/article/details/61415263,foreach只是语法糖,本质还是
for(Iterator i$ = a.iterator(); i$.hasNext(); System.out.print(temp));

然后追踪栈轨迹看看源码:

@SuppressWarnings("unchecked")
public E next() {
    checkForComodification();//问题出在这行代码,然后看这个方法是什么?
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

上面两个方法都是private class Itr implements Iterator 这个迭代器实现类的方法。有上面两个方法可以明显的看出 if (modCount != expectedModCount)这个判断上,是这两个值不相等导致抛出了异常,那么这两个值又是什么呢?

//The number of times this list has been <i>structurally modified</i>.
protected transient int modCount = 0;

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

modCount变量是ArrayList继承的抽象类AbstractList中一个变量,它表示的是对list的修改次数,而迭代器中的expectedModCount 在初始的时候,将modCount赋给了expectedModCount ,通过上面foreach我们看得到迭代器是在已进入for循环的时候初始的这时候modCount是未进行修改的,但是接下来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;
}

如上代码,看到remove的元素如果存在的话会进入下面这个方法:

private void fastRemove(int index) {
    modCount++;//在这里操作了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
}

看到这里我们明白了什么时候改变了modCount导致和expectedModCount 不一致抛出了错误。其实,就是java基于fast-fail机制,当集合内部结构发生预期外变化时及时的抛出错误,方便我们检测程序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值