最近项目中遇到一个异常问题,需求是转变数据库中的格式,需要循环每个list并对其进行add或remove长度增减操作,代码编译没问题,但运行后系统报错:
java.util.ConcurrentModificationException: null at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) ~[na:1.8.0_77] at java.util.HashMap$EntryIterator.next(HashMap.java:1463) ~[na:1.8.0_77] at java.util.HashMap$EntryIterator.next(HashMap.java:1461) ~[na:1.8.0_77]于是决定记录下问题,总结原因和解决方法。
在ArrayList的父类AbstractList的源码中有iterator()迭代方法,其中实现为返回一个新建Itr()对象:
public Iterator<E> iterator() { return new Itr(); }继续进入Itr()方法,AbstractList中一个内部成员类:
protected transient int modCount = 0; private 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(); } }在这个具体实现类中,有以下四个成员变量:
cursor:后续调用next返回元素的索引;lastRet:最近调用返回的元素的索引,如果调用remove删除该元素,则重置为-1;expectedModCount:对ArrayList修改次数的期望值,初始值为modCount;modCount:list的修改次数。调用add()和remove()方法就会对modCount进行加一操作当调用递归方法时,总是会先调用checkForComodification()方法,然后根据cursor的值获取到元素,之后将cursor的值付给lastRet,并对cursor的值进行加一操作。初始时,cursor=0,lastRet=-1,那么调用一次之后,cursor=1,lastRet=0。注意此时,modCount=0,expectedModCount=0。
而在remove()方法中则是对modCount进行加一操作。然后接下来就是删除元素的操作,最后将size进行减一操作,并将引用置为null以方便垃圾收集器进行回收工作。那么注意此时各个变量的值:对于iterator,其expectedModCount=0,cursor=1,lastRet=0。对于list,其modCount=1,size=0。接着看程序代码,执行完删除操作后,继续while循环,调用hasNext()方法判断,由于此时cursor=1,而size=0,那么返回true,所以继续执行while循环,然后继续调用iterator的next()方法。
如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。很显然,此时modCount=1,而expectedModCount=0,因此程序就抛出了ConcurrentModificationException异常。因为在递归中调用list.remove()方法导致modCount和expectedModCount的值不一致。
通过查看源码可以发现在AbstractList类中调用listIterator()方法,其底层ListItr()的add()方法中expectedModCount = modCount;这时 checkForComodification()方法就不会再报错。所以如果业务场景中需要迭代对list进行增删元素时,使用ListIterator对象,调用他的remove()或add()方法即可。