给出一个代码场景:
public static void main(String args[]) {
List<String> list =new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for(String str : list) {
list.remove(str);
}
}
当执行这段代码时,会抛出异常
这是为什么呢?代码看起来没有任何的问题。
分析:
for(String str : list) 这里使用了迭代器,即
Iterator itr = list.iterator();
while
(itr.hasNext()) {
String str = (String)itr.next();
}
迭代器的接口这样定义:
public
Iterator<E> iterator() {
return
new
Itr();
}
而Itr这样定义:
private
class
Itr
implements
Iterator<E> {
int
cursor;
// index of next element to return
int
lastRet = -
1
;
// index of last element returned; -1 if no such
int
expectedModCount = modCount;
Itr() {}
public
boolean
hasNext() {
return
cursor != size;
}
@SuppressWarnings
(
"unchecked"
)
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];
}
......
}
重点关照expectedModCount与 modCount。
这里的modCount代表ArrayList的修改次数,而expectedModCount代表的是迭代器的修改次数,在创建Itr迭代器的时候,将modCount赋值给了expectedModCount,在此场景下两个变量初值均为4.
但当进行remove操作时,modCount的次数在增加,而expectedModCount没有改变。当调用next()时,首先调用checkForComodification()
检查两值是否相等,发现不相等,那么抛出异常。(函数内抛出异常)
我们再来思考下,为什么要有这个检测呢?这个异常到底起到什么作用呢?简单理解就是不允许一个线程在修改集合,另一个线程在集合基础之上进行迭代。一旦检测到了这种情况就会通过fast-fail机制,抛出异常,防止后面的不可知状况。
如果非要修改,那么也可以自己定义一个迭代器,内部实现去除checkForComodification()
函数,当然不建议这么做。