Iterator接口使用时并发修改异常的解决过程
相信很多小伙伴通过对JavaSE基础内容的学习,已经对List集合有了较好的掌握,那接着就请大家跟着来回顾一下List相关的内容吧。大家知道,我们在遍历List集合时,通常会使用Iterator接口来迭代集合中的元素。但是,如果我们对Iterator接口使用不当,就可能会导致并发修改异常,即java.util.ArrayList$Itr.checkForComodification异常。那这个checkForComodification异常到底是如何产生的呢?接下来就请大家来跟随袁老师的步伐,一点点揭开它的神秘面纱吧。
一. 什么是异常
首先,简单带大家回顾一下异常的相关知识。我们举个例子好,就好比人人都希望自己身体健康,遇到的事情都能顺利解决,但在实际生活中总会遇到各种状况,比如感冒发烧,工作时电脑蓝屏、死机等。
同样,在程序运行的过程中,也会发生各种非正常的状况。例如,程序运行时磁盘空间不足、网络连接中断、被装载的类不存在等。针对这种情况, Java语言给我们引入了异常处理机制,以异常类的形式对这些非正常情况进行封装,利用异常处理机制对程序运行时发生的各种问题进行处理。
接下来我们通过一个案例来认识一下什么是异常。
public class Example01 {
public static void main(String[] args) {
int result = divide(4, 0); // 调用divide()方法
System.out.println(result);
}
// 下面的方法实现了两个整数相除
public static int divide(int x, int y) {
int result = x / y; // 定义一个变量result记录两个数相除的结果
return result; // 将结果返回
}
}
上述代码的运行结果如下图所示:
从运行结果可以看出,这个程序发生了算术异常(ArithmeticException),该异常是由于调用divide()方法时传入了参数0,运算时出现了被0除的情况。该异常发生后,程序就会立即结束,无法继续向下执行。
二. Iterator接口
我们在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,Java专门提供了一个Iterator接口。Iterator接口也是集合中的一员,但它与Collection、Map接口有所不同。Collection接口与Map接口主要用于存储元素,而Iterator则主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。
接下来我们再通过一个案例,来学习如何使用Iterator迭代集合中的元素。
import java.util.ArrayList;
import java.util.Iterator;
public class Example02 {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 创建ArrayList集合
list.add("张三"); // 向该集合中添加字符串
list.add("李四");
list.add("王五");
list.add("赵六");
Iterator it = list.iterator(); // 获取Iterator对象
while (it.hasNext()) { // 判断ArrayList集合中是否存在下一个元素
Object obj = it.next(); // 取出ArrayList集合中的元素
System.out.println(obj);
}
}
}
当遍历元素时,首先通过调用ArrayList集合的iterator()方法来获得迭代器对象;然后使用hasNext()方法判断集合中是否存在下一个元素。如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。需要注意的是,在通过next()方法获取元素时,我们必须保证要获取的元素存在,否则,就会抛出NoSuchElementException异常。
Iterator迭代器在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者更好地理解迭代器的工作原理,接下来袁老师再通过一个图例来演示Iterator对象迭代元素的过程。
上图中,我们在调用Iterator的next()方法之前,迭代器的索引位于第一个元素之前,不指向任何元素。当第一次调用迭代器的next()方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回。当再次调用next()方法时,迭代器的索引会指向第二个元素并将该元素返回。然后以此类推,直到hasNext()方法返回false,表示到达了集合的末尾,终止对元素的遍历。
通过迭代器获取ArrayList集合中的元素时,这些元素的类型都是Object类型。如果我们想获取到特定类型的元素,则需要进行对数据类型强制转换。
通过对上述案例的分析,想必各位小伙伴对如何使用Iterator接口来迭代集合中的元素,已经掌握的很深入了。那么如果我们在使用Iterator迭代集合元素时,如果使用不当就可能会产生并发修改的异常,下面就再请大家来跟随袁老师的思路,咱们一块儿来探索这个问题。
三. 并发修改异常
在使用Iterator迭代器对集合中的元素进行迭代时,如果我们调用了集合对象的remove()方法去删除元素之后,继续使用迭代器遍历元素,就会出现异常。
下面我们通过一个案例来演示这种异常。假设在一个集合中存储了学校里所有学生的姓名,由于一个名为“张三”的学生中途转学,这时就需要在迭代集合时找出该元素并将其删除,具体代码如下。
import java.util.ArrayList;
import java.util.Iterator;
public class Example03 {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 创建ArrayList集合
list.add("张三");
list.add("李四");
list.add("王五");
Iterator it = list.iterator(); // 获得Iterator对象
while (it.hasNext()) { // 判断该集合是否有下一个元素
Object obj = it.next(); // 获取该集合中的元素
if ("张三".equals(obj)) { // 判断该集合中的元素是否为张三
list.remove(obj); // 删除该集合中的元素
}
}
System.out.println(list);
}
}
上述程序在运行时出现了并发修改异常ConcurrentModificationException,这个异常是由迭代器对象抛出的。而出现该异常的原因是集合在迭代器运行期间删除了元素,这会导致迭代器预期的迭代次数发生改变,导致迭代器的结果不准确。
要解决上述问题,我们可以采用两种方式,下面袁老师会对这两种方式分别进行介绍。
第一种方式:从业务逻辑上讲,我们只想将姓名为“张三”的学生删除,至于后面还有多少学生并不关心。所以我们只需找到该学生跳出循环不再迭代即可,也就是可以在第12行代码下面增加一个break语句,代码如下:
if ("张三".equals(obj)) {
list.remove(obj);
break;
}
第二种方式:如果我们需要在迭代集合期间对集合中的元素进行删除,可以使用迭代器本身的删除方法,将第12行代码替换成it.remove()方法,即可解决这个问题:
if ("张三".equals(obj)) {
it.remove();
}
由运行结果可知,学员“张三”确实被删除了,且没有出现异常。因此我们可以得出结论,当调用迭代器对象的remove()方法来删除元素,会导致迭代次数的变化,这对于迭代器对象本身来讲是可预知的。
猜你喜欢LIKE
相关推荐HOT
更多>>servlet底层原理是什么?
1、ServletAPI核心类与接口2、Servlet类处理请求的流程创建servlet类的步骤:创建一个命名为TestServlet继承javax.servlet.http.HttpServlet类详情>>
2023-05-30 10:41:22多线程的优势与劣势分别是什么?
多线程是指在同一个程序中,同时运行多个线程,每个线程都可以独立执行不同的任务,相互之间不会干扰。多线程的优势和劣势如下:优势:提高程序...详情>>
2023-05-30 10:32:12设计模式之生产者与消费者的代码实现
本文主要讲述生产者和消费者模式,文中会使用通俗易懂的案例,使你更好地学习本章知识点并理解原理,做到有道无术。什么是生产者和消费者模式生...详情>>
2023-05-30 10:25:46从零开始学Java之interface接口
一.接口简介简介Java中的接口(interface)类似于是一种特殊的抽象类,它也是众多抽象方法的集合。接口的定义方式、组成部分都与抽象类相似,却比...详情>>
2023-05-29 11:26:17热门推荐
如何进行mysql数据备份?
沸什么是servlet的生命周期?servlet请求处理流程是怎样的?
热servlet底层原理是什么?
热怎样编写java程序?
新多线程的优势与劣势分别是什么?
ssm框架的作用与原理是什么?
设计模式之生产者与消费者的代码实现
接口和抽象类有什么区别?4个方面对比
从零开始学Java之interface接口
从零开始学Java之Java中的内部类是怎么回事?
一分钟带你了解MySQL——基础与介绍
在java中,super关键字怎样使用
什么是事件流以及事件流的传播机制 ?
弹性盒有哪些属性是在父元素身上?