</pre><p><span style="font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px; color:rgb(0,0,255)"><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">工作中碰到个ConcurrentModificationException。代码如下:</span></span></p><p><span style="font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px; color:rgb(0,0,255)">List list = ...;</span></p><span style="font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px; color:rgb(0,0,255)">for(Iterator iter = list.iterator(); iter.hasNext();) {<br style="" /> Object obj = iter.next();<br style="" /> ...<br style="" /> if(***) {<br style="" /> list.remove(obj);<br style="" /> }<br style="" />}<br style="" /></span><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">在执行了remove方法之后,再去执行循环,iter.next()的时候,报java.util.ConcurrentModificationException(当然,如果remove的是最后一条,就不会再去执行next()操作了)</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">下面来看一下源码</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px; color:rgb(0,0,255)">public interface Iterator<E> {<br style="" /> boolean hasNext();<br style="" /> E next();<br style="" /> void remove();<br style="" />}<br style="" /><br style="" />public interface Collection<E> extends Iterable<E> {<br style="" /> ...<br style="" /> Iterator<E> iterator();<br style="" /> boolean add(E o);<br style="" /> boolean remove(Object o);<br style="" /> ...<br style="" />}</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">这里有两个remove方法</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">接下来来看看AbstractList</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px; color:rgb(0,0,255)">public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { <br style="" />//AbstractCollection和List都继承了Collection<br style="" /> protected transient int modCount = 0;<br style="" /> private class Itr implements Iterator<E> { //内部类Itr<br style="" /> int cursor = 0;<br style="" /> int lastRet = -1;<br style="" /> int expectedModCount = modCount;<br style="" /><br style="" /> public boolean hasNext() {<br style="" /> return cursor != size();<br style="" /> }<br style="" /><br style="" /> public E next() {<br style="" /> checkForComodification(); <span style="color:rgb(255,0,0)">//特别注意这个方法<br style="" /></span> try {<br style="" /> E next = get(cursor);<br style="" /> lastRet = cursor++;<br style="" /> return next;<br style="" /> } catch(IndexOutOfBoundsException e) {<br style="" /> checkForComodification();<br style="" /> throw new NoSuchElementException();<br style="" /> }<br style="" /> }<br style="" /><br style="" /> public void remove() {<br style="" /> if (lastRet == -1)<br style="" /> throw new IllegalStateException();<br style="" /> checkForComodification();<br style="" /><br style="" /> try {<br style="" /> AbstractList.this.remove(lastRet); <span style="color:rgb(255,0,0)">//执行remove对象的操作<br style="" /></span> if (lastRet < cursor)<br style="" /> cursor--;<br style="" /> lastRet = -1;<br style="" /> expectedModCount = modCount; <span style="color:rgb(255,0,0)">//重新设置了expectedModCount的值,避免了ConcurrentModificationException的产生<br style="" /></span> } catch(IndexOutOfBoundsException e) {<br style="" /> throw new ConcurrentModificationException();<br style="" /> }<br style="" /> }<br style="" /><br style="" /> final void checkForComodification() {<br style="" /> if (modCount != expectedModCount) <span style="color:rgb(255,0,0)">//当expectedModCount和modCount不相等时,就抛出ConcurrentModificationException</span><br style="" /> throw new ConcurrentModificationException();<br style="" /> }<br style="" /> } <br style="" />}</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">remove(Object o)在ArrayList中实现如下:</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px; color:rgb(0,0,255)">public boolean remove(Object o) {<br style="" /> if (o == null) {<br style="" /> for (int index = 0; index < size; index++)<br style="" /> if (elementData[index] == null) {<br style="" /> fastRemove(index);<br style="" /> return true;<br style="" /> }<br style="" /> } else {<br style="" /> for (int index = 0; index < size; index++)<br style="" /> if (o.equals(elementData[index])) {<br style="" /> fastRemove(index);<br style="" /> return true;<br style="" /> }<br style="" /> }<br style="" /> return false;<br style="" />}<br style="" />private void fastRemove(int index) {<br style="" /> modCount++; <span style="color:rgb(255,0,0)">//只增加了modCount<br style="" /></span> ....<br style="" />}<br style="" /><span style="color:rgb(51,153,102)"><br style="" /><span style="color:rgb(128,0,0)">所以,产生ConcurrentModificationException的原因就是:<br style="" />执行remove(Object o)方法之后,modCount和expectedModCount不相等了。然后当代码执行到next()方法时,判断了checkForComodification(),发现两个数值不等,就抛出了该Exception。<br style="" />要避免这个Exception,就应该使用remove()方法。</span></span><span style=""><br style="" /></span></span><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">这里我们就不看add(Object o)方法了,也是同样的原因,但没有对应的add()方法。一般嘛,就另建一个List了</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><p>同理以下的方法也是错误的:</p><p>for(String s : strList){ strList.remove(s);//这种方法是错误的。ConcurrentModificationException }</p><p></p><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">下面是网上的其他解释,更能从本质上解释原因:</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。</span><br style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px" /><p><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px">所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。</span></p><p><span style="color:rgb(75,75,75); font-family:verdana,Arial,helvetica,sans-seriff; font-size:13px; line-height:20.799999237060547px"></span></p><p><span style="font-family:verdana,Arial,helvetica,sans-seriff; color:#4b4b4b"><span style="font-size:12.727272033691406px; line-height:20.799999237060547px">之前有一个项目从数据库中取出的对象中,有些是空的,也就是取不到。再对这些对象进行操作之前,必须的判空,这样感觉有些麻烦,那还不如直接删除list中的空对象。如何删除list的空对象呢?</span></span></p><p><span style="font-family:verdana,Arial,helvetica,sans-seriff; color:#4b4b4b"><span style="font-size:12.727272033691406px; line-height:20.799999237060547px">在java中对list进行操作很频繁,特别是进行list启遍历,这些操作我们都会,也很熟悉,但是对java中list进行删除元素,remove list中的元素就不怎么熟悉了吧,可以说很陌生,是实际操作中也很容易出错,先看看下面这个java中如何remove list 中的元素吧.</span></span></p><p><span style="font-family:verdana,Arial,helvetica,sans-seriff; color:#4b4b4b"><span style="font-size:12.727272033691406px; line-height:20.799999237060547px"></span></span></p><p><span style="font-family:verdana,Arial,helvetica,sans-seriff; color:#4b4b4b"><span style="font-size:12.727272033691406px; line-height:20.799999237060547px"></span></span></p><pre name="code" class="java" style="color: rgb(75, 75, 75); font-size: 12.727272033691406px; line-height: 20.799999237060547px;"> List<String> strList = new ArrayList<String>();
strList.addAll(Arrays.asList("zhb","hb","xxx"));
for(int i = 0; i < strList.size(); i++){
strList.remove(i);
}
print(strList);//hb还存在为什么会这样呢?
原因:List每remove掉一个元素以后,后面的元素都会向前移动,此时如果执行i=i+1,则刚刚移过来的元素没有被读取。
怎么解决?有三种方法可以解决这个问题:
1.倒过来遍历list
Java代码
for (int i = list.size()-1; i > =0; i--) {
if (((String) list.get(i)).startsWith("abcde")) {
list.remove(i);
}
}
2.每移除一个元素以后再把i移回来
Java代码
for (int i = 0; i < list.size(); i++) {
if (((String) list.get(i)).startsWith("abcde")) {
list.remove(i);
i=i-1;
}
}
3.使用iterator.remove()方法删除
Java代码
for (Iterator it = list.iterator(); it.hasNext();) {
String str = (String)it.next();
if (str.equals("chengang")){
it.remove();
}
}
本文详细解析了Java List遍历时并发修改导致的ConcurrentModificationException异常产生的原因及解决方案,包括Iterator的工作原理、异常抛出机制、避免方法等,并通过实例展示了如何正确地在遍历时移除元素而不引发异常。
727

被折叠的 条评论
为什么被折叠?



