关于使用迭代器对集合进行遍历时,不能对集合进行修改的论证

关于使用迭代器对集合进行遍历时,不能对集合进行修改的论证## 标题 ##

摘要:迭代器再帮助我们进行对集合的元素进行遍历提供了有效的方法,java采用迭代器模式能在不暴露集合对象内部元素的情况下,对元素进行访问。

1.使用迭代器的优点

  1. Collection中的remove()方法需要先查找到需要删除元素的位置,这本身就需要一定的开销
  2. 如果在使用迭代器进行对集合的遍历时,对集合自身产生结构上的变化的时候(add.remove,clear等),例如:遍历时对集合当前向的下一项做了删除的操作,当再一次调用next(),就会出现混乱,下文会从源码的角度对其进行分析。

2.接口Iterator源码

public interface Iterator<E> {
    //获取下一个元素,第一次调用给出第一项,第二次给出第二项,。。。
     E next();
     //是否存在下一个,存在true,不存在false
     boolean hasNext();
     //从底层集合中删除迭代器返回的最后一个元素,就是next()返回的集合中的元素
     default void remove() {
        throw new UnsupportedOperationException("remove");
     }
     //对每个剩余的元素进行一定的操作
     default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }

}

3.测试实例

    @Test
    public void  test_Iterator() {
        List<String> list = new ArrayList<>();
        list.add("唐三藏");
        list.add("孙悟空");
        list.add("猪八戒");
        list.add("沙和尚");
        list.add("白龙马");
        for(Iterator<String> iter = list.iterator();iter.hasNext();{
            if("猪八戒".equals(iter.next())) {
                list.add("高小姐");
            }
        }
    }

测试结果:java.util.ConcurrentModificationException

4.源码解读

  private class Itr implements Iterator<E> {//Itr类作为ArrayList的内部类
    int cursor;       // 下一个元素的索引
    int lastRet = -1; // 最后一个元素索引
    int expectedModCount = modCount;//预期集合元素数量
    public boolean hasNext() {
        //size外部类集合的大小
        return cursor != size;
    }
    @SuppressWarnings("unchecked")
    public E next() {
          //关键一步,调用checkForComodification()会去校验expectedModCount 和modCount是否相等,不等抛异常
          //modCount 字段作为外部类结构修改次数的记录,为子类提供快速迭代,上文实例抛错就是出自此处
        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();
    }
}

源码解读:对集合进行迭代器遍历的时候,调用hashNext(),查看下一个元素的索引是否和集合的大小相等,相等表示已经到了集合的末尾,不存在下一个元素返回false;调用next()方法会先调用checkForComodification()方法来确保对集合修改次数一致,不一致抛异常,如果此时我们调用集合的remove(int index),就会去修改modCount 字段的次数,等下次再调用迭代器的next()方法的时候,值就会不一致 ,出现异常。同时,从逻辑的角度上来说,当我们对集合进行迭代的时候,如果对集合进行结构上的修改,直接会影响迭代器的遍历,产生不可预知的结果,所以在迭代器进行遍历的时候集合是不能对集合自身进行修改的。

发布了10 篇原创文章 · 获赞 5 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览