本篇先来一个练习题,引出什么是并发修改异常,然后给出解决这个问题的方案。什么是并发修改异常呢?如果我们在遍历集合元素,并进行添加操作的时候,就会报并发修改异常错误,下面我们来举例一下。
1. 练习题
在一个集合中,如果找到一个元素是”world”,那么就添加一个“Java”的元素。下面,我们根据前面的集合知识,尝试写出代码应该如下面这样。
package list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Demo2_List {
public static void main(String[] args) {
List list= new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("c");
list.add("d");
//用迭代器遍历
Iterator it = list.iterator(); //获取迭代器
while(it.hasNext()) {
if("world".equals(it.next())) {
list.add("java"); //这个时候,我们只能想起用add方法
}
}
System.out.println(list);
}
}
运行输出:
Exception in thread "main" java.util.ConcurrentModificationException
atjava.util.ArrayList$Itr.checkForComodification(Unknown Source)
atjava.util.ArrayList$Itr.next(Unknown Source)
atlist.Demo2_List.main(Demo2_List.java:20)
在JAVA JDK 1.6 API中搜索ConcurrentModificationException这个类,可以看到这么一句话:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
上面用通俗的语言来分析就是这样的,我们有list这个集合,当前是有5个元素。然后就把自己有5个元素信息传递给了迭代器。在遍历的过程中,又出现了需要添加元素,但是迭代器只认为有5个元素进行遍历,现在突然添加元素到集合,迭代器就不知道怎么办了,就抛出并发修改的异常。为了解决这个异常,我们下面学习一个新的类,叫ListIterator。
package list;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo2_List {
public static void main(String[] args) {
List list= new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("c");
list.add("d");
//用迭代器遍历
ListIterator it = list.listIterator(); //获取迭代器
while(it.hasNext()) {
if("world".equals(it.next())) {
it.add("java"); //注意是用listiterator迭代器添加元素
}
}
System.out.println(list);
}
}
输出:
[a, b, world, java, c, d]
下面,我们在API找到ListIterator,看看有哪些方法,并写代码进行测试。上面解决并发修改异常,我们用到了这个接口的add()方法,下面看看其他方法的基本使用。
package list;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo3_ListIterator {
public static void main(String[] args) {
List list= new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("c");
list.add("d");
//用迭代器遍历
ListIteratorl it= list.listIterator(); //获取迭代器
//正向遍历
while(lit.hasNext()) {
System.out.println(lit.next());
}
System.out.println("----------------");
//逆向遍历
while(lit.hasPrevious()) {
System.out.println(lit.previous());
}
}
}
输出:
a
b
world
c
d
----------------
d
c
world
b
a
上面是hasPrevious()方法和previous(),这个刚好和前面集合学习的正向遍历方法相反,是把集合从后往前遍历的方法。这里需要注意一个问题,如果前面我们不写hasNext()遍历,直接使用hasPrevious(),输出的是空白,因为一开始指针指向是索引0的元素,0前面肯定没有其他的元素,索引hasPrevious()返回是false,就不会继续while循环。这种反向遍历,在实际工作中应该没有什么作用,使用机会不多。剩下的remove()和set()就不联系,和前面的介绍的删除元素和修改元素一样的功能。