迭代器的快速失败
collection的iterator方法返回的迭代器都是快速失败的:创建迭代器后,我们先了解iterator迭代器,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出 ConcurrentModificationException。因此调用map.remove(“tom”),进入下次循环后,直接抛出ConcurrentModificationException异常
**下面输出的代码就是一个快速失败的例子:
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add(1);
arr.add(2);
arr.add(3);
arr.add(4);
Iterator<Integer>ito=arr.iterator();
while (ito.hasNext()){
int a=ito.next();
arr.remove(a);
System.out.println(arr);
}
}
输出结果:
Exception in thread "main" java.util.ConcurrentModificationException
如何避免快速失败:
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add(1);
arr.add(2);
arr.add(3);
arr.add(4);
Iterator<Integer>ito=arr.iterator();
while (ito.hasNext()){
int a=ito.next();
if(a==2){
**ito.remove();**
}else{
System.out.println(a);
}
System.out.println(arr);
}
}
输出结果为:
1
3
4
下边我们进ArrayList源码进行分析:
ArrayList定义了一个内部类实现Iterator接口,内部类需要实现接口中的方法,
(2)ArrayList中的iterator()方法是返回内部类
private class Itr implements Iterator<E> {
int cursor; //下一个元素的索引
int lastRet = -1; //expectedModCount上一个元素的索引,没有的话就返回-1
int expectedModCount = modCount; //expectedModCount相当于常量
//判断下一个元素的索引有没有到达数组的容量大小,达到了就没有了
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
// 一下获取当前索引的元素的方法next,并指向下一个元素的索引
public E next() {
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];
}
// revome(该方法的底层实现不涉及modCount++操作)
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
在其中调用了checkRorComdification();方法判断集合结构是否发生了改变。(快速失败)
// checkRorComdification()方法,判断集合是否发生改变
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
总结:medCount用来记录集合被修改的次数,再利用迭代器遍历时需要判断modCount 与expectedModCount(相当于常量)是否相等,如果相等则,可以继续遍历,否则会迭代失败,抛出异常java.util.ConcurrentModificationException。
解决方法就是在迭代过程中去用调用迭代器自身的方法。