查错ConcurrentModificationException

title: 查错ConcurrentModificationException date: 2017.12.12 22:18 categories:

  • 技术博客 tags:
  • J2SE
  • 集合框架

本文阐述该异常排错的思路,主要涉及到J2SE中集合框架的基础内容,解决思路:上网查+理解JDK源码。

起因

某我手把手带学Java的同学来问我这个问题:使用Iterator迭代List中,使用list.remove()时报了ConcurrentModificationException这个异常,原因是什么呢?我为什么不能删除集合中的某个元素呢?

应该要表扬他的表述能力,清晰表达了问题的上下文,也带出了他自己的疑虑,这点有助于我来定位问题。

经过

建议先阅读这篇cnblog上面的博客,如果你能懂,就没必要继续阅读本文。

首先这个问题需要简单了解涉及到的API,即各个类与内部使用到的方法实现。此例中可查看:

  • ArrayList
  • ItrListItr同理)

其实都在一个类中,后者是前者的内部类,此处的内部类是一种代码高内聚的封装手法,在阅读源码时,注意力主要集中在两个成员上:

  • AbstractList中的protected transient int modCount = 0 负责记录集合元素修改的次数,而ArrayList继承了这个抽象类,因而是直接的成员变量
  • Itr中的int expectedModCount = modCount 负责记录迭代器修改元素的次数

注意他俩的初始值,前者是0,后者从前者来。

ArrayListIterator之间的关系,用大白话讲,一个负责装东西,一个负责数东西,所以前者是一个容器,后者是一个计数器(模拟cursor)。好比是,前者是跳绳子的人,后者是数跳了多少下的人。

那么,自己跳多少次跟帮你计数的人的结果是否应该相等呢?

逻辑上应该是相等的,JDK代码中也是这么做的,所以后者初始化成员时要从前者赋值。

而在迭代器的修改元素的方法中,首先要调用checkForComodification()来确保这个逻辑一致,实现上就是比较两个count数值。

理解到了这一步,我们回头看下报错的场景,此时使用的不是Itr中的remove()而是ArrayList中的。

这就好比,跳绳子的人之前一直是匀速跳,某一秒慢了一倍速,结果上跳绳次数就应当-1,但数的人并没发觉,逻辑上,他下一次数的时候,已经是错误的计数值了。体现到代码中,就是抛出异常。

在代码中,list.remove()从数组中去掉了某元素,modCount也-1,但因为此时使用了迭代器遍历,而Itr即迭代器实现类中的expectedModCount并没有-1,二者不等:

if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
复制代码

简言之:使用迭代器遍历list时,会创建一个集合线程+一个计数线程,每次开始前会检查二者对应的修改次数是否等,不等即报错。

引申Javaforeach在遍历时,底层同样使用了Iterator来计数,所以应当注意不使用list.remove(),正确的,应该使用iterator.remove()。关于这点,C#中更加清晰,可以了解下C# 运算符重载

小结

  • 命名可读性:本例中涉及到的JDK源码从命名上极其具有可读性,即使不清楚业务实现,根据名字也会猜测到用意,如ConcurrentModificationException正翻译为并发修改异常,与我们上述结论相符。
  • 理解类中成员的作用:成员包括变量与方法,前者存储数据状态,后者根据业务场景更新状态。
  • 排错的好心态与清晰的思路:报错解决掉后就可以提高一点个人能力,同时过程中要充分利用工具、资源。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值