Iterator接口使用时并发修改异常的解决过程
相信很多小伙伴通过对JavaSE基础内容的学习,已经对List集合有了较好的掌握,那接着就请大家跟着袁老师来回顾一下List相关的内容吧。大家知道,我们在遍历List集合时,通常会使用Iterator接口来迭代集合中的元素。

但是,如果我们对Iterator接口使用不当,就可能会导致并发修改异常,即java.util.ArrayList$Itr.checkForComodification异常。那这个checkForComodification异常到底是如何产生的呢?接下来就请大家来跟随袁老师的步伐,一点点揭开它的神秘面纱吧。
01 什么异常
首先,袁老师简单带大家回顾一下异常的相关知识。我们举个例子好,就好比人人都希望自己身体健康,遇到的事情都能顺利解决,但在实际生活中总会遇到各种状况,比如感冒发烧,工作时电脑蓝屏、死机等。
同样,在程序运行的过程中,也会发生各种非正常的状况。例如,程序运行时磁盘空间不足、网络连接中断、被装载的类不存在等。针对这种情况, Java语言给我们引入了异常处理机制,以异常类的形式对这些非正常情况进行封装,利用异常处理机制对程序运行时发生的各种问题进行处理。
接下来我们通过一个案例来认识一下什么是异常。
上述代码的运行结果如下图所示:

从运行结果可以看出,这个程序发生了算术异常(ArithmeticException),该异常是由于调用divide()方法时传入了参数0,运算时出现了被0除的情况。该异常发生后,程序就会立即结束,无法继续向下执行。
02 Iterator接口
我们在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,Java专门提供了一个Iterator接口。Iterator接口也是集合中的一员,但它与Collection、Map接口有所不同。Collection接口与Map接口主要用于存储元素,而Iterator则主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。
接下来我们再通过一个案例,来学习如何使用Iterator迭代集合中的元素。
当遍历元素时,首先通过调用ArrayList集合的iterator()方法来获得迭代器对象;然后使用hasNext()方法判断集合中是否存在下一个元素。如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。需要注意的是,在通过next()方法获取元素时,我们必须保证要获取的元素存在,否则,就会抛出NoSuchElementException异常。
Iterator迭代器在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者更好地理解迭代器的工作原理,接下来袁老师再通过一个图例来演示Iterator对象迭代元素的过程。

上图中,我们在调用Iterator的next()方法之前,迭代器的索引位于第一个元素之前,不指向任何元素。当第一次调用迭代器的next()方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回。当再次调用next()方法时,迭代器的索引会指向第二个元素并将该元素返回。然后以此类推,直到hasNext()方法返回false,表示到达了集合的末尾,终止对元素的遍历。
通过迭代器获取ArrayList集合中的元素时,这些元素的类型都是Object类型。如果我们想获取到特定类型的元素,则需要进行对数据类型强制转换。
通过袁老师对上述案例的分析,想必各位小伙伴对如何使用Iterator接口来迭代集合中的元素,已经掌握的很深入了。那么如果我们在使用Iterator迭代集合元素时,如果使用不当就可能会产生并发修改的异常,下面就再请大家来跟随袁老师的思路,咱们一块儿来探索这个问题。
03 并发修改异常
在使用Iterator迭代器对集合中的元素进行迭代时,如果我们调用了集合对象的remove()方法去删除元素之后,继续使用迭代器遍历元素,就会出现异常。
下面我们通过一个案例来演示这种异常。假设在一个集合中存储了学校里所有学生的姓名,由于一个名为“张三”的学生中途转学,这时就需要在迭代集合时找出该元素并将其删除,具体代码如下。
上述程序在运行时出现了并发修改异常ConcurrentModificationException,这个异常是由迭代器对象抛出的。而出现该异常的原因是集合在迭代器运行期间删除了元素,这会导致迭代器预期的迭代次数发生改变,导致迭代器的结果不准确。

要解决上述问题,我们可以采用两种方式,下面袁老师会对这两种方式分别进行介绍。
第一种方式:从业务逻辑上讲,我们只想将姓名为“张三”的学生删除,至于后面还有多少学生并不关心。所以我们只需找到该学生跳出循环不再迭代即可,也就是可以在第12行代码下面增加一个break语句,代码如下:
第二种方式:如果我们需要在迭代集合期间对集合中的元素进行删除,可以使用迭代器本身的删除方法,将第12行代码替换成it.remove()方法,即可解决这个问题:
由运行结果可知,学员“张三”确实被删除了,且没有出现异常。因此我们可以得出结论,当调用迭代器对象的remove()方法来删除元素,会导致迭代次数的变化,这对于迭代器对象本身来讲是可预知的。
04 回顾总结
关于Iterator接口并发修改异常的解析,袁老师就给大家介绍到这里,最后我们再来回顾下这一小节的主要内容。
首先,袁老师给大家介绍了什么是异常;然后,介绍了Iterator是如何遍历List集合的;最后,袁老师给大家介绍了使用Iterator进行集合元素遍历时,可能产生的并发修改问题,以及如何防止该问题的产生。
好了,本文的内容袁老师就给大家介绍到这里了,你学会了吗?关注「袁庭新」,干货天天都不断!
