迭代器并发修改异常问题
本文档记录迭代器并发修改问题,并手撕底层源码分析解决问题。
问题概述:
已知集合,要求实现使用iterator迭代器遍历,遍历到其中所有的这个特定元素(假设是 柯南2)时,增加一个元素(柯南死神小学生)
问题源码
public class Test02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("柯南1");
list.add("柯南2");
list.add("柯南3");
list.add("柯南4");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();//报错位置
if(element.equals("柯南2")){
list.add("柯南死神小学生");
}
}
for (String element : list) {
System.out.println(element);
}
}
}
报错类型
ConcurrentModificationException
API中的描述:
当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。
ConcurrentModificationException类在util包下继承于RuntimeException(运行时异常)
手撕源码
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>{
//其中一个参数,modCount = 0;
protected transient int modCount = 0;
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
//分析第五步:
//it.next();分析出只要那两个参数不相等就会报错,继续分析
//if(element.equals("柯南2"))
// list.add("柯南死神小学生");
//执行了if里边的add后modCount变成5了,而初始化迭代器时expectedModCount就定死了是4.
//至此,分析结束
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//.....省略部分
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//-----------------------
//ok,这个鬼东西加1了
//.....省略部分
}
//分析第一步:
//String element = it.next();
//Iterator<String> it = list.iterator();
//报错位置最先调用的list.iterator()创建了一个对象
//相当于Iterator<String> it = new Itr();
public Iterator<E> iterator() {
return new Itr();
}
//分析第二步:
//调用了it.next(),it本质是new Itr()的对象
//Itr是内部类
private class Itr implements Iterator<E> {
//其中第二个参数,执行到报错位置前执行了4次add
//所以现在这两个参数都是4(add操作会使modCount+1,看第五步分析)
int expectedModCount = modCount;
public E next() {
//分析第三步:
//进入next,按顺序执行checkForComodification();
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
//分析第四步:
//可以看到两个变量modCount expectedModCount找到它们
//这两个鬼东西不同就会报并发修改异常,现在就找哪步让它们不相等
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
实现该需求的方法
//不用迭代器
for (int i=0;i<list.size();i++){
String e = list.get(i);
if (e.equals("柯南2")){
list.add(i+1,"柯南死神小学生");
}
}
若是需求改成删除柯南2,那么只需要将list.remove("柯南2");
换成it.remove("柯南2");
因为Itr()重写了remove方法
//更改需求后实现方法
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();//报错位置
if(element.equals("柯南2")){
it.remove("柯南2");
}
}