对JAVA集合进行遍历删除时务必要用迭代器
2017-08-15 00:00
519 查看
几行类似这样的代码:
运行出异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.bes.Test.main(Test.java:15)
Java新手最容易犯的错误,对JAVA集合进行遍历删除时务必要用迭代器。切记。
其实对于如上for循环,运行过程中还是转换成了如下代码:
仍然采用的是迭代器,但删除操作却用了错误的方法。如将famous.remove(s)改成it.remove()
则运行正常,结果也无误。
为什么用了迭代码器就不能采用famous.remove(s)操作? 这种因为ArrayList与Iterator混合使用时会导致各自的状态出现不一样,最终出现异常。
现象解释:
ArrayList()的 iterator()方法,返回一个Itr对象的实例,他是ArrayList类内部一个私有的内部类:
public Iterator<E> iterator() {
return new Itr();
}
基本上ArrayList采用size属性来维护自已的状态,而Iterator采用cursor来来维护自已的状态。
当size出现变化时,cursor并不一定能够得到同步,除非这种变化是Iterator主动导致的。
从上面的代码可以看到当Iterator.remove方法导致ArrayList列表发生变化时,他会更新cursor来同步这一变化。但其他方式导致的ArrayList变化,Iterator是无法感知的。ArrayList自然也不会主动通知Iterator们,那将是一个繁重的工作。Iterator到底还是做了努力:为了防止状态不一致可能引发的无法设想的后果,Iterator会经常做checkForComodification检查,以防有变。如果有变,则以异常抛出,所以就出现了上面的异常。
ArrayList采用modCount来标记自己被修改的次数,而Iterator采用expectedModCount来标记自己被修改的次数,检测如果发现不一致,就会异常。
public static void main(String args[]) { List<String> famous = new ArrayList<String>(); famous.add("liudehua"); famous.add("madehua"); famous.add("liushishi"); famous.add("tangwei"); for (String s : famous) { if (s.equals("madehua")) { famous.remove(s); } } }
运行出异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.bes.Test.main(Test.java:15)
Java新手最容易犯的错误,对JAVA集合进行遍历删除时务必要用迭代器。切记。
其实对于如上for循环,运行过程中还是转换成了如下代码:
for(Iterator<String> it = famous.iterator();it.hasNext();){ String s = it.next(); if(s.equals("madehua")){ famous.remove(s); } }
仍然采用的是迭代器,但删除操作却用了错误的方法。如将famous.remove(s)改成it.remove()
则运行正常,结果也无误。
为什么用了迭代码器就不能采用famous.remove(s)操作? 这种因为ArrayList与Iterator混合使用时会导致各自的状态出现不一样,最终出现异常。
现象解释:
ArrayList()的 iterator()方法,返回一个Itr对象的实例,他是ArrayList类内部一个私有的内部类:
public Iterator<E> iterator() {
return new Itr();
}
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(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
基本上ArrayList采用size属性来维护自已的状态,而Iterator采用cursor来来维护自已的状态。
当size出现变化时,cursor并不一定能够得到同步,除非这种变化是Iterator主动导致的。
从上面的代码可以看到当Iterator.remove方法导致ArrayList列表发生变化时,他会更新cursor来同步这一变化。但其他方式导致的ArrayList变化,Iterator是无法感知的。ArrayList自然也不会主动通知Iterator们,那将是一个繁重的工作。Iterator到底还是做了努力:为了防止状态不一致可能引发的无法设想的后果,Iterator会经常做checkForComodification检查,以防有变。如果有变,则以异常抛出,所以就出现了上面的异常。
ArrayList采用modCount来标记自己被修改的次数,而Iterator采用expectedModCount来标记自己被修改的次数,检测如果发现不一致,就会异常。
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
相关文章推荐
- 对JAVA集合进行遍历删除时务必要用迭代器
- 对JAVA集合进行遍历删除时务必要用迭代器 推荐
- 对JAVA集合进行遍历删除时务必要用迭代器
- 对JAVA集合进行遍历删除时务必要用迭代器
- 对JAVA集合进行遍历删除时务必要用迭代器
- 对JAVA集合进行遍历删除时务必要用迭代器
- 对JAVA集合进行遍历删除时务必要用迭代器
- 对JAVA集合进行遍历删除时务必要用迭代器
- 对JAVA集合进行遍历删除时务必要用迭代器
- Java-集合 list对象进行指定位置插入、删除、迭代、遍历输出(具体习题讲解)
- Java-集合 list对象进行指定位置插入、删除、迭代、遍历输出(具体习题讲解)
- Java使用迭代器遍历集合,遍历过程中可删除元素
- java中使用迭代器进行遍历集合的注意点
- java里面在遍历集合的时候对集合进行添加或者删除修改时的并发修改异常
- java中循环遍历删除List和Set集合中元素的方法(推荐)
- Java集合怎么一边删除一边遍历
- java中循环遍历删除List和Set集合中元素的方法
- Java基础知识强化之集合框架笔记07:Collection集合的遍历之迭代器遍历
- java中List集合遍历和迭代器遍历
- java集合系列二:多种方法进行collection遍历,并封装为公共方法