foreach
阿里巴巴java开发手册
【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
反例
List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) {
if("1".equals(temp)){
a.remove(temp);
}
}
正例
Iterator<String> it= a.iterator();
while(it.hasNext()){
String temp = it.next();
if(删除元素的条件){
it.remove();
}
}
foreach源码
foreach遍历集合,其实是走的Iterator,首先判断hasNext(),如果没有了则终止循环,否则next()获取元素时,next()时,都要check一下集合元素个数是否变化了,如果变化了,则抛出异常。
Itr是ArrayList的内部类,实现了Iterator接口
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;
public boolean hasNext() {
return cursor != size;//游标不等于元素个数就是还有下一个
}
public E next() {
checkForComodification();//check是否并发修改
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
modCount是集合添加元素、删除元素的次数,expectedModCount是预期的修改次数。增删操作会使得modCount+1,不等于expetedModCount,所以抛出异常。
没有使用list.iterator时调用的是ArrayList自己的remove,并不会同步这两个值,导致抛出异常。调用了ArrayList.iterator之后,然后了Itr对象,此后再remove,remove方法中有让这两个值相等的操作。
迭代器方式移除
那为什么Iterator不会异常呢?
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();
}
}
迭代器的remove方法会修改expectedModCount,从而使modCount与之相等
参考:https://blog.csdn.net/wangjun5159/article/details/61415358