Java深入理解 - 迭代器 Iterator 总结

来源:互联网 发布:网络浙江卫视在线直播 编辑:程序博客网 时间:2024/05/22 16:52

今天一位朋友在用迭代器时很郁闷为什么会会报Java.util.ConcurrentModificationExceptiond 异常, 于是写下这篇博客,想详细的讲讲Java里面  的迭代器.

           Iterator简单的来说就是遍历, 遍历什么? 遍历集合元素等.  

          Iterator接口共有四个方法:         

public interface Iterator<E>{       boolean hasNext():如果被迭代的集合还元素没有被遍历,则返回true。    Object next():返回集合里下一个元素。    void remove() :删除集合里上一次next方法返回的元素    void forEachRemaining(Consumer action),这是Java 8为Iterator新增的默认方法,该方法可使用Lambda表达式来遍历集合元素。}  }


 如果只是普通的迭代输出,那么不会出现什么问题.但是在现实需求中这往往没有什么用,我们还需要做一些额外的操作,比如说

 add/remove等。 但是这样做很可能会出现一些很尴尬的事情, 下面我给出一段正确的迭代删除代码:

import java.util.*;public class TestIterator{    public static void main(String[] args)    {        // 创建集合、添加元素        Collection<String> books = new ArrayList();        books.add("测试1");        books.add("测试2");        books.add("测试3");           Iterator it = books.iterator();        while(it.hasNext())        {            // it.next()方法返回的数据类型是Object类型,因此需要强制类型转换            String book = (String)it.next();            System.out.println(book);            if (book.equals("测试2"))            {                // 从集合中删除上一次next方法返回的元素                it.remove();            }        }        System.out.println(books);    }}

上面的代码经过测试是可以正确删除元素的,有疑问的代码估计也就是这一句了
  Iterator it = books.iterator();

这段代码的意思是 获得List对象的迭代器,然后通过迭代器来遍历List对象内保存的元素.

接着我们就可以调用他的 hasNext方法和next方法去操作他了.


接着贴出一段错误的代码这是在我工作机上面测试的代码 jdk是1.8 (当然我相信以前的jdk结果应该也是这样):
 
 
那么为什么二种删除方式一个有错一个没错呢(这个问题难倒了工作三年的一个朋友).  经过刚刚的测试,我们发现用迭代器本身删除是可以的,但是在迭代器中

对list本身进行删除就不行了, 对于这种莫名其妙的问题,我们从源码入手, 首先根据上面的我们先看看ArrayList他是怎么返回Iterator实例的,最终源码如下:



 在ArrayList类中有一个私有类Private Itr 我们注意到expectedModCount这个字段,他的初始值是等于modCount的。 那modCount又是干啥的呢 最直接的。 看源码:

 

我们从源码可以对集合的每一次 add / remove 都会触发一下这个字段,而上面Itr的remove 不管在删除还是添加都会触发一个方法(csdn上截图太麻烦我直接写了 )   checkForComodification();这个方法, 大家可以看看上面的截图而这个 checkForComodification();方法中只有一个功能

 if (modCount != expectedModCount)                throw new ConcurrentModificationException();

 那就是抛出异常~~~~

所以大家应该懂了那段错误的代码为什么会错误了吧。。 因为二个字段不相等啊。 没有同步啊。


如果你问我为什么要这么做,我只能这么跟你说:

Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性

   

 

   


  

1 0