美文网首页java
Java使用for和迭代器Iterator中remove比较

Java使用for和迭代器Iterator中remove比较

作者: HachiLin | 来源:发表于2022-06-12 15:47 被阅读0次

    1. Iterator介绍

      对于java中的集合类(Collection),可以使用迭代器Iterator对集合中的元素进行遍历。迭代器是一种设计模式,它可以在不暴露集合中元素的情况下而去遍历集合中的所有元素。
      Iterator为一个接口,只定义了三个方法,hasNext(),next(),和remove()。Collection接口继承Iterable接口,提供了一个iterator()方法,使得Collection子类通过iterator()方法获取Collection内部实现的Iterator对象。

    2. for循环删除容器元素误区

      正常来说,如果我们需要删除容器中某个特定元素,直接想到的方法就是使用for循环遍历,拿下面例子来说:

        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        
        for (String str : list) {
             if ("b".equals(str))
                list.remove(str);
             else
                System.out.println(str);
            }
        System.out.println(list);
    

    此时允许代码会抛出如下异常:

    Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
        at java.util.ArrayList$Itr.next(ArrayList.java:859)
    

    查看859行代码和909行代码:

    858        public E next() {
    859          checkForComodification();
    ...          ...    
            }
    ...
    907       final void checkForComodification() {
    908          if (modCount != expectedModCount)
    909             throw new ConcurrentModificationException();
    ...     }
    

      next()是ArrayList内部实现Iterator的方法,改方法在859行调用了checkForComdification方法。该方法在909行为什么抛出异常呢?原因在于list执行remove操作的时候,"b"的位置已经被"c"取代,list上的元素做了移动(具体可以查看ArrayList的remove源码),而remove操作导致了modCount++,这时迭代器中的等号已经不成立了,故抛出异常。

      综上,原因就在于Java中的for each实际上使用的是iterator进行处理的。而iterator是不允许集合在iterator使用期间删除的。所以导致了iterator抛出了ConcurrentModificationException

      如果对于for each还是不甘心的,可以使用一个容器将删除元素先暂存起来,然后执行完遍历后,使用removeAll操作也可以达到预期效果。

    3. Iterator迭代器删除

    先来看下使用迭代器删除例子:

        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        
        Iterator<String> iter = list.iterator();
        while (iter.hasNext()) {
           String str = iter.next();
           if ("b".equals(str))
              iter.remove();
           else
              System.out.println(str);
        }
        System.out.println(list);
    

    程序正常运行输出:

    a
    c
    d
    [a, c, d]
    

    这里之所以不会和for each报相同错误得益于它的remove方法,查看Iterator的迭代器remove源码:

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

    会发现,Iterator在使用ArrayList自身的remove方法之后,执行了expectedModCount = modCount,这也就保证了Iterator在执行next的方法等时候两者的等号是成立的。

    4. 参考文献

    相关文章

      网友评论

        本文标题:Java使用for和迭代器Iterator中remove比较

        本文链接:https://www.haomeiwen.com/subject/awwgmrtx.html