Java——迭代器iterator详解

Java——迭代器iterator详解一 Iterator 的 API nbsp nbsp nbsp 关于 Iterator 主要有三个方法 hasNext next remove nbsp nbsp nbsp hasNext 没有指针下移操作 只是判断是否存在下一个元素 nbsp nbsp nbsp next 指针下移 返回该指针所指向的元素 nbsp nbsp nbsp remove 删除当前指针所指向的元素 一般和 next 方法一起用 这时候的作用就是删除 next 方法返回的元素 nbsp 二

一、Iterator的API

      关于Iterator主要有三个方法:hasNext()、next()、remove()

      hasNext:没有指针下移操作,只是判断是否存在下一个元素

      next:指针下移,返回该指针所指向的元素

      remove:删除当前指针所指向的元素,一般和next方法一起用,这时候的作用就是删除next方法返回的元素

 

二、Iterator原理

迭代器是将集合的数据放到一个容器中并排成一排,iterator有一个游标,最初的时候,游标在第一个元素前面,调用Iterator.next()是将游标往后移一位,Iterator.hasNext()是判断游标后面还没有可以迭代的元素。

 

三、为什么用迭代器?

因为最初的时候你用for循环遍历List,之后人家又要你遍历Set,但是for循环无法遍历Set,因为Set是无序的(无法get值),所以后面就统一用迭代器遍历集合了。

 

四、迭代器为什么不定义成一个类,而是一个接口?

假如迭代器是一个类,我们就可以创建该类的对象,然后调用该类的方法来遍历集合。

但是,Java定义了很多的集合类,他们的数据结构不相同,所以他们存储的方式和遍历的方法也应该是不相同的。所以最终就没有定义迭代器类。

而无论你是哪种集合,你都应该具备获取元素的操作。并且,最好辅助于判断功能,这样先判断再获取,就不会出错。也就是说,判断和获取功能应该是集合所具备的,而因为不同集合的数据结构不一样,所以他们的判断方式也不一样。我们把这两种功能抽象出来为,不提供具体实现,就是接口。

 

五、如何实现迭代器?

在真正的具体类中,以内部类的方式体现出来。

每个集合类内部都含有一个内部类,用来实现Iterator接口。集合类的iterator方法就是在获取内部类对象(迭代器对象),然后通过该对象调用hashNext和next方法实现遍历。-

class ArrayList{ public Iterator<E> iterator(){return new Itr();} class Itr implements Iterator{ public hasNext(){……}//重写这两个方法,实现元素遍历 public next(){……} } } 

通过内部类,我们才可以使用诸如list.iterator().hasNext()这样的方法。

 

六、迭代器迭代顺序

HashMap按键的顺序,HashSet按hashCode的顺序。

 

七、为什么被迭代的对象不允许被改变?

用迭代器本身删除是可以的,但是在迭代器中对集合本身进行删除就不行了, 对于这种莫名其妙的问题,我们从源码入手

Java——迭代器iterator详解

modCount是当前集合的版本号,每次修改(增、删)集合都会加1;expectedModCount是当前迭代器的版本号,在迭代器实例化时初始化为modCount。我们看到在checkForComodification()方法中就是在验证modCount的值和expectedModCount的值是否相等,所以当你在调用了ArrayList.add()或者ArrayList.remove()时,只更新了modCount的状态,而迭代器中的expectedModCount未同步,因此才会抛出异常。

Java——迭代器iterator详解

但是为什么使用Iterator.remove()就没有问题呢?

在Iterator的remove()中同步了expectedModCount的值,所以当你下次再调用next()的时候,检查不会抛出异常。

 

那么为什么要这样呢? 

迭代器是工作在一个独立的线程中,并且拥有一个mutex锁,就是说iterator在工作的时候,是不允许被迭代的对象被改变的。

iterator被创建的时候建立一个内存索引表(单链表),这个索引表指向原来的对象,当原来的对象数量改变的时候,这个索引表的内容没有同步改变,所以当索引指针往下移的时候,便找不到要迭代的对象,于是错误。

List、Set等是动态的,可变对象的数量的数据结构,但是iterator则是单向不可变的,只能顺序读取,不能逆序操作的数据结构,当iterator指向的原始数据发生变化时,iterator自己就迷失了方向。

 

这个机制的意义在哪里?

使用该机制的主要目的是为了实现ArrayList中的快速失败机制(fail-fast),在Java集合中较大一部分集合是存在快速失败机制的。

快速失败机制产生的条件:当多个线程对Collection进行操作时,若其中某一个线程通过Iterator遍历集合时,该集合的内容被其他线程所改变,则会抛出ConcurrentModificationException异常。

 

那么多线程访问容器的过程中抛出ConcurrentModificationException异常的话又该咋办呢?

  • 在JDK1.5版本中引入了线程安全的容器,比如ConcurrentHashMap和CopyOnWriteArrayList等,可以使用这些线程安全的容器来代替非线程安全的容器。
  • 在使用迭代器遍历容器的时候对容器的操作放到synchronized代码块中,但是当引用程序并发成都比较高的时候,这会严重影响程序的性能。

 

八、Foreach

实现了Iterable接口的类就可以通过Foreach遍历,那是因为foreach要依赖于Iterable接口返回的Iterator对象,所以从本质上来讲,Foreach其实就是在使用迭代器,在使用foreach遍历时对集合的结构进行修改,和在使用Iterator遍历时对集合结构进行修改本质上是一样的。所以同样的也会抛出异常,执行快速失败机制。

 

九、for循环与迭代器的比较

每个方法都有不同的语境,因此它们没有绝对的好也没有绝对的坏,因此在效率上各有各的优势:

  • ArrayList对随机访问比较快,而for循环中使用的get()方法,采用的即是随机访问的方法,因此在ArrayList里for循环快。
  • LinkedList则是顺序访问比较快,Iterator中的next()方法采用的是顺序访问方法,因此在LinkedList里使用Iterator较快。
     

引申:Iterator与ListIterator有什么区别?

Iterator只能正向遍历集合,适用于获取移除元素。ListIerator继承自Iterator,专门针对List,可以从两个方向遍历List,同时支持元素的修改。

 

参考博客:

https://blog.csdn.net/liyancheng/article/details/

https://blog.csdn.net/hellowordapi/article/details/

https://blog.csdn.net/xiakexiaohu/article/details/

https://blog.csdn.net/shuaishuai3409/article/details/

https://blog.csdn.net/Searchin_R/article/details/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232509.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • android app反编译_安卓反编译教程

    android app反编译_安卓反编译教程在学习Android开发的过程你,你往往会去借鉴别人的应用是怎么开发的,那些漂亮的动画和精致的布局可能会让你爱不释手,作为一个开发者,你可能会很想知道这些效果界面是怎么去实现的,这时,你便可以对改应用的APK进行反编译查看。下面是我参考了一些文章后简单的教程详解。(注:反编译不是让各位开发者去对一个应用激活成功教程搞重装什么的,主要目的是为了促进开发者学习,借鉴好的代码,提升自我开发水平。)测试环

    2025年7月3日
    3
  • 经济基础知识(初级)【17】

    经济基础知识(初级)【17】1.党的十八大报告中概括的科学发展观最鲜明的精神实质包括()A.解放思想B.实事求是C.与时俱进D.求真务实E.城乡协调2.下来金融业务中,属于投资银行业务的有()A.为工商企业代理证券买卖B.向工商企业提供中长期贷款C.投资工商企业股票D.参与工商企业并购重组E.向消费者提供消费贷款3.下来会计要素中,反映企业财务状况的有()A.收入B.资产C.负债D.费用E.所有者权益4.下来数据整理与显示方法中,适用于顺序数据的有()A.圆形图B.直方图C.累积

    2022年5月28日
    41
  • linux安装windows系统_deepin安装wine

    linux安装windows系统_deepin安装wine一、安装1.参考网站:https://www.jianshu.com/p/809c0de6fc31https://github.com/askme765cs/Wine-QQ-TIM2.步骤:先下载WineQQ压缩包:wineQQ9.0.3_23729.tar.xz$sudoadd-apt-repositoryppa:wine/wine-builds$sudoapt-get…

    2025年9月20日
    5
  • c语言rand函数生成随机数,详解C语言生成随机数rand函数的用法[通俗易懂]

    说到rand函数,大家是不是会和EXCEL中的rand函数混淆,当小编第一次接触的时候也以为是EXCEL的函数,本文是爱站技术频道小编为大家带来的详解C语言生成随机数rand函数的用法,一起来看看吧!函数rand()是真正的随机数生成器,而srand()会设置供rand()使用的随机数种子。如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。而使用同种子…

    2022年4月18日
    47
  • pycharm调用模块怎么import不了_python路径添加

    pycharm调用模块怎么import不了_python路径添加configureinterpreters下添加链接路径及源码路径全部打钩点击目标文件,勾选源码,即可在文件中import添加使用

    2022年8月29日
    4
  • java视频上传与播放功能

    java视频上传与播放功能参考:https://wenku.baidu.com/view/63fb1da364ce0508763231126edb6f1aff0071c7.html此次上传播放功能采用eclipse+ssm+maven+spring2.5+tomcat8+jdk8这个功能是有一定缺陷的,CKplayer播放视频需要H264MP4格式的,所以需要格式转化,网盘上都有。需要源码的百度网盘地址:…

    2022年7月7日
    24

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号