对JAVA集合进行遍历删除时务必要用迭代器

今天同事写了几行相似这样的代码:java

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);
        }
    }
}

运行出异常:ide

Exception in thread "main" java.util.ConcurrentModificationExceptionthis

at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)spa

at java.util.AbstractList$Itr.next(AbstractList.java:343)element

at com.bes.Test.main(Test.java:15)rem

Java新手最容易犯的错误,对JAVA集合进行遍历删除时务必要用迭代器。切记。get

其实对于如上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()it

则运行正常,结果也无误。io

固然若是改为:

for (int i = 0; i < famous.size(); i++) {
            String s = famous.get(i);
            if (s.equals("madehua")) {
                famous.remove(s);
            }
        }

这种方法,也是能够完成功能,但通常也不这么写。

为何用了迭代码器就不能采用famous.remove(s)操做? 这种由于ArrayList与Iterator混合使用时会致使各自的状态出现不同,最终出现异常。

咱们看一下ArrayList中的Iterator实现:

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 {
       E next = get(cursor);
       lastRet = cursor++;
       return next;
       } catch (IndexOutOfBoundsException e) {
       checkForComodification();
       throw new NoSuchElementException();
       }
   }
   public void remove() {
       if (lastRet == -1)
       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();
   }
   }

基本上ArrayList采用size属性来维护自已的状态,而Iterator采用cursor来来维护自已的状态。

当size出现变化时,cursor并不必定可以获得同步,除非这种变化是Iterator主动致使的。

从上面的代码能够看到当Iterator.remove方法致使ArrayList列表发生变化时,他会更新cursor来同步这一变化。但其余方式致使ArrayList变化,Iterator是没法感知的。ArrayList天然也不会主动通知Iterator们,那将是一个繁重的工做。Iterator到底仍是作了努力:为了防止状态不一致可能引起的没法设想的后果,Iterator会常常作checkForComodification检查,以防有变。若是有变,则以异常抛出,因此就出现了上面的异常。

相关文章
相关标签/搜索