您的位置:首页 > 其它

集合操作的ConcurrentModificationException异常分析,为什么有时候循环remove不会异常?Iterator方式也会异常吗?

2016-10-11 14:52 731 查看
在我们代码中,禁止在foreach遍历list的时候直接使用list.remove()方法来删除元素的,会ConcurrentModificationException。不同的集合使用不同的方式,ArrayList,Vector,CopyOnWriteArrayList*
**单线程情况:如果实际应用场景下有在遍历时删除元素的需求,如果容器为ArrayList或者Vector请使用Iterator中的remove()方法;若果是CopyOnWriteArrayList 可以直接使用for循环remove,注意CopyOnWriteArrayList
使用Iterator的remove反而会报错。追根溯源大家看源码吧。**1.先看一下面代码?会产生ConcurrentModificationException?```public
static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("a");
list.add("b"); list.add("c"); for (String s : list) { if ("b".equals(s))
{ list.remove(s); } }
System.out.println(list);}```2.会产生ConcurrentModificationException?```public
static void main(String[] args) { List<String> list = new CopyOnWriteArrayList<String>(); list.add("a");
list.add("b"); list.add("c"); for (String s : list) { if ("b".equals(s))
{ list.remove(s); } }
System.out.println(list);}```3.会产生ConcurrentModificationException?```public
static void main(String[] args) { List<String> list = new Vector<String>(); list.add("a");
list.add("b"); list.add("c"); for (String s : list) { if ("b".equals(s))
{ list.remove(s); } }
System.out.println(list);}```实际的结果是,程序运行完成,输出[a,
c],因为删除的是倒数第二个元素。如果杀出的不是倒数第二个元素,只有第二段代码是正常运行的。下面看一下ArrayList的遍历:```
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned;
-1 if no such int expectedModCount = modCount; public boolean hasNext() {
return cursor != size; } @SuppressWarnings("unchecked")
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];
} public void remove() { if (lastRet < 0)
throw new IllegalStateException(); checkForComodification(); try {
ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount
= modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException();
} } final void checkForComodification() {
if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
}```遍历时每当Itr去获取下一个元素时,就会调用checkForComodification()去检查ArrayList是否被修过,如果被修改过就会抛出ConcurrentModificationException,调用ArrayList中的remove方法时,modCount被加1,所以会造成modCount的值与expectedModCount不相等。所以当Itr尝试去获取下一个元素时,一定会抛ConcurrentModificationException。看remove的源码:```
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(index); return true; } }
return false; } /*
* Private remove method that skips bounds checking and does not * return the value removed. */
private void fastRemove(int index) { 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 }```**那么又是为什么程序在删除"b"元素之后没有抛这个异常呢?----------------------------**```public
boolean hasNext() { return cursor != size();}```**这个方法,
当元素"b"被删除后,cursor的值正好与数组的size()相等,所以Itr的遍历任务已经结束,不再会去获取下一个元素,所以也就没有抛异常了。****当然这个情况只有在删除数组中的倒数第二个元素时才会出现,只是一个巧合现象,在我们代码规范中,还是绝对禁止在遍历list的时候直接使用list.remove()方法来删除元素的。**#总结:*
**如果实际应用场景下有在遍历时删除元素的需求,如果容器为ArrayList或者Vector请使用Iterator中的remove()方法;若果是CopyOnWriteArrayList
可以直接使用for循环remove,注意CopyOnWriteArrayList 使用Iterator的remove反而会报错。追根溯源大家看源码吧。更好的方式是不要直接修改原来的集合类容器,取出需要的元素丢到新的容器返回给需要使用的场景------------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐