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

在我们代码中,禁止在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反而会报错。追根溯源大家看源码吧。更好的方式是不要直接修改原来的集合类容器,取出需要的元素丢到新的容器返回给需要使用的场景------------------------------------------------------------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值