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

今天一位朋友在用迭代器时很郁闷为什么会会报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() 方法会在删除当前迭代对象的同时维护索引的一致性

   

 

   


  

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值