在读阿里Java开发手册时其中集合处理篇
【强制】 不要在 foreach 循环里进行元素的 remove/add 操作。 remove 元素请使用 Iterator
方式,如果并发操作,需要对 Iterator 对象加锁。
正例:
List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (删除元素的条件) { iterator.remove(); } }
反例:
for (String item : list) { if ("1".equals(item)) { list.remove(item); } }
说明: 以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1” 换成“2” ,会是同样的结果吗?
上面代码正例、反例打印出的结果是一致的,看不出异常,但按照说明把“1”改为“2”,反例报错了,报java.util.ConcurrentModificationException。
调查了下报异常的原因是:
源码中每次foreach迭代的时候都有两部操作:
第一步:iterator.hasNext() //判断是否有下个元素
public boolean hasNext() { return cursor != size; }
第二步:item = iterator.next() //将下个元素赋值给item
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]; }
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
java.util.ConcurrentModificationException由checkForComodification抛出,原因为 modCount != expectedModCount。
进一步阅读源码,发现:
1.modCount 时List从new开始,被修改的次数。当List调用Remove等方法时,modCount++
2.expectedModCount是指Iterator现在期望这个list被修改的次数是多少次。是在Iterator初始化的时候将modCount 的值赋给了expectedModCount
那么就解释了为什么会报上述异常:
1.modCount 会随着调用List.remove方法而自动增减,而expectedModCount则不会变化,就导致modCount != expectedModCount。
2.在删除倒数第二个元素后,cursor=size-1,此时size=size-1,导致hasNext方法认为遍历结束。
所以在 foreach 循环里进行元素的 remove/add 操作, remove 元素请使用 Iterator。
Mark!