java.util.ConcurrentModificationException异常分析

在读阿里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!

猜你喜欢

转载自www.cnblogs.com/wawadao/p/12760461.html
今日推荐