简单说明:ArrayList 在 For 循环中进行删除而产生异常的原因

简单说明:ArrayList 在 For 循环中进行删除而产生异常的原因经常会有人这么对 list 进行遍历,错而不自知。示例代码如下:public static void main(String[] args) {

欢迎大家来到IT世界,在知识的湖畔探索吧!

经常会有人这么对 list 进行遍历,错而不自知。

示例代码如下:

public static void main(String[] args) {
 List<String> list = new ArrayList<>();
 list.add("aaa");
 list.add("bbb");
 list.add("ccc");
 list.add("ddd");
 for (String str : list) {
 if ("aaa".equals(str)) {
 list.remove("aaa");
 }
 }
}

欢迎大家来到IT世界,在知识的湖畔探索吧!

以上代码执行导致的报错信息如下:

欢迎大家来到IT世界,在知识的湖畔探索吧!Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
 at java.util.ArrayList$Itr.next(ArrayList.java:851)
 at demo.service.impl.test.main(test.java:14)

网上有很多博客对此都做了说明,这篇文章通过比较浅显易懂的方式说明报错产生的原因。

一、list.add

list.add 代码执行时,有一个变量发生改变了,那就是 modCount。在代码中 list.add 共执行4次,所以 modCount 的值为 4。

注:list add()、remove() clear() 都会改变 modCount 值。

二、for (String str : list)

for (String str : list) 调用的是 ArrayList 中内部类 ItrItr 是对 Iterator 的实现。而在 Iterator 开始前,会先执行 int expectedModCount = modCount

此时 expectedModCount modCount 均为 4

三、list.remove(“aaa”)

在此处先看一下会报错的原因,以下是源码:

final void checkForComodification() {
 if (modCount != expectedModCount)
 throw new ConcurrentModificationException();
}

modCount expectedModCount 不相等了,所以报错。

有人可能会跟我有一样的想法,为什么 list.remove(“aaa”) 时,不把 expectedModCount = modCount 重新赋值一次。其实是有的,只是调用的方法错了。

例子中 list.remove(“aaa”) 调用的 remove 源码如下:

欢迎大家来到IT世界,在知识的湖畔探索吧!public boolean remove(Object o) {
 if (o == null) {
 for (int index = 0; index < size; index++)
 if (elementData[index] == null) {
 fastRemove(index);
 return true;
 }
 } else {
 for (int index = 0; index < size; index++)
 if (o.equals(elementData[index])) {
 // 示例中调用的是此处的 fastRemove
 fastRemove(index);
 return true;
 }
 }
 return false;
}

而使 modCount 的值改变的是其中的 fastRemove 方法。

fastRemove 源码如下:

private void fastRemove(int index) {
 // 此处 modCount + 1
 modCount++;
 int numMoved = size - index - 1;
 if (numMoved > 0)
 System.arraycopy(elementData, index+1, elementData, index,
 numMoved);
 elementData[--size] = null; // clear to let GC do its work
}

而真正使 expectedModCount = modCount 执行的源码如下:

public void remove() {
 if (lastRet < 0)
 throw new IllegalStateException();
 checkForComodification();
 try {
 AbstractList.this.remove(lastRet);
 if (lastRet < cursor)
 cursor--;
 lastRet = -1;
 expectedModCount = modCount;
 } catch (IndexOutOfBoundsException e) {
 throw new ConcurrentModificationException();
 }
}

此代码在内部类 Itr 中。

这也就是为什么会说,如果 list 在循环中有删除操作,最好用 iterator 迭代的方式去做。

四、总结

简单总结一下

  • list.remove() 没有对 expectedModCount 重新赋值
  • iterator.remove()expectedModCount 重新赋值

建议大家跟踪一下源代码,代码量不多,也很容易理解。

简单说明:ArrayList 在 For 循环中进行删除而产生异常的原因

rivate class Itr implements Iterator<E> {
 /**
 * Index of element to be returned by subsequent call to next.
 */
 int cursor = 0;
 /**
 * Index of element returned by most recent call to next or
 * previous. Reset to -1 if this element is deleted by a call
 * to remove.
 */
 int lastRet = -1;
 /**
 * The modCount value that the iterator believes that the backing
 * List should have. If this expectation is violated, the iterator
 * has detected concurrent modification.
 */
 int expectedModCount = modCount;
 public boolean hasNext() {
 return cursor != size();
 }
 public E next() {
 checkForComodification();
 try {
 int i = cursor;
 E next = get(i);
 lastRet = i;
 cursor = i + 1;
 return next;
 } catch (IndexOutOfBoundsException e) {
 checkForComodification();
 throw new NoSuchElementException();
 }
 }
 public void remove() {
 if (lastRet < 0)
 throw new IllegalStateException();
 checkForComodification();
 try {
 AbstractList.this.remove(lastRet);
 if (lastRet < cursor)
 cursor--;
 lastRet = -1;
 expectedModCount = modCount;
 } catch (IndexOutOfBoundsException e) {
 throw new ConcurrentModificationException();
 }
 }
 final void checkForComodification() {
 if (modCount != expectedModCount)
 throw new ConcurrentModificationException();
 }
}

作者:饭娱咖啡

本文为云栖社区内容,未经允许不得转载。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/37557.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信