for 循环 报类转换异常_简单说明:ArrayList 在 For 循环中进行删除而产生异常的原因...

经常会有人这么对 list 进行遍历,错而不自知。

示例代码如下:

public static void main(String[] args) { List 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:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at demo.service.impl.test.main(test.java:14)

网上有很多博客对此都做了说明,这篇文章通过比较浅显易懂的方式说明报错产生的原因。

一、list.add

list.add 代码执行时,有一个变量发生改变了,那就是 modCount。在代码中 list.add 共执行4次,所以 modCount 的值为 4。

注:list add()、remove() clear() 都会改变 modCount 值。

二、for (String str : list)

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

此时 expectedModCount modCount 均为 4

三、list.remove("aaa")

在此处先看一下会报错的原因,以下是源码:

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

modCount expectedModCount 不相等了,所以报错。

有人可能会跟我有一样的想法,为什么 list.remove("aaa") 时,不把 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])) { // 示例中调用的是此处的 fastRemove fastRemove(index); return true; } } return false;}

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

fastRemove 源码如下:

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}

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

public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); }}

此代码在内部类 Itr 中。

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

四、总结

简单总结一下

  • list.remove() 没有对 expectedModCount 重新赋值
  • iterator.remove()expectedModCount 重新赋值

建议大家跟踪一下源代码,代码量不多,也很容易理解。

b83c5e4d9bab674f97102c3e2b0ae4db.png
rivate class Itr implements Iterator { /** * Index of element to be returned by subsequent call to next. */ int cursor = 0; /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1; /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ int expectedModCount = modCount; public boolean hasNext() { return cursor != size(); } 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(); } } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }}

作者:饭娱咖啡

本文为云栖社区内容,未经允许不得转载。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值