fail-fast以及Iterator对象
Iterator对象在foreach循环的时候只能使用自己的remove/add方法,如果使用了集合的remove/add方法或者其他线程并发时修改了集合的内容,就属于fail-fast机制,即是一个错误,但是该错误会尽最大可能地抛出ConcurrentModificationException异常,该异常应尽量作为一个bug的检测。
public class Test1 {
public static void main(String[] args) {
List<String> ls=new ArrayList<String>();
ls.add("Bob");
ls.add("Jia");
for(Iterator it=ls.iterator();it.hasNext();) {//这里的Iterator对象是一个List对象的另一种形式,头指针不包含元素,只包含头指针。每次的next方法都会输出下一个元素。
if ("Bob".equals(it.next())) {//这里会得到"Bob"。
ls.remove(0);//这里使用ls来移除或添加,那么操作过后总长度是变了的,会和Iterator对象长度不符,因此调用Iterator对象的next等方法会抛出Concurrent...异常。
System.out.println(it.next());//所以这里会抛出异常,不管是ls移除任意位置索引。除非Iterator调用自身方法移除队头元素即用it来移除就是remove()方法无参数,表示移除。
//但是如果把上述调用it.next()给注释掉,就不会抛出异常。当没有用hasNext来检验时,调用next()方法可能该元素不存在,会抛出NoSuchElementException异常。
//如果是并发操作,记得给Iterator对象上锁哦。
//通俗一点理解就是iterator方法就是说我要遍历ls的那几个元素我都做上记号,如果ls把他们删除或添加了,我这个Iterator遍历的范围同样随之改变(就是说我的next方法会动态改变),但是增加元素我这里是没有记号的,删除元素我这里有记号但是你没有元素也对不上,记号就对不上了自然会抛出异常。而Iterator对象本身的移除方法则是说我不遍历你了,就没有记号对不上一说。
}
}
}
总结。
准确的说,在调用迭代器的时候,会有两个属性modCount和expectedModCound他们的值相等,当使用迭代器的remove方法移除集合元素时,不仅修改modCount的值,还会修改到expectedModCount的值使他们同步变化,而调用集合的移除或添加方法则只会修改mc的值,不会修改emc的值。这两个值不相等时就会抛出ConcurrentModificationException异常。
再总结。
iterator和集合假设为list的remove()方法都会删除掉集合里的对象,list有属性modCount,在利用iterator()方法的时候会创建一个单链索引表指向集合对象,但是list删除对象不会同步该单链索引表,因为该表是iterator里面的东西,可以把单链索引表看作expectedModCount,由此他们不匹配,会抛出异常。而如果利用iterator删除对象,不仅会删除集合里的元素修改modCount的值,还会同步索引表,也就是同步expectedModCount的值。上述的索引表相当于是一个引用,指向集合对象。该引用保存在Iterator对象里。我调用iterator的remove()方法实际上就是利用该引用来删除集合元素,又利用该引用得到删除元素后的集合的modCount值,将其赋给expectedModCount,由此实现匹配。而利用list的remove方法我这个iterator是感知不到的,而next方法和hasNext方法只会判断expectedModCount和modCound是否相等,相等则执行,否则抛出ConcurentModificationException异常。
抛出ConcurrentModificationException异常的原因