Java集合【2】--iterator接口详解
一、iterator
接口介绍
iterator
接口,也是集合大家庭中的一员。和其他的Map
和Collection
接口不同,iterator
主要是为了方便遍历集合中的所有元素,用于迭代访问集合中的元素,相当于定义了遍历元素的规范,而另外的Map
和Collection
接口主要是定义了存储元素的规范。
还记得么?之前说的iterable
接口,有一个方法就是叫iterator()
,也是返回iterator
对象。
迭代:不断访问集合中元素的方式,取元素之前先判断是否有元素,有则取出来,没有则结束,不断循环这个过程,直到遍历完里面所有的元素。
但是值得注意的是,集合类的整体不是继承了iterator
接口,而是继承了iterable
接口,通过iterable
接口的方法返回iterator
的对象。值得注意的是,iterator
的remove()
方法,是迭代过程中唯一安全的修改集合的方法,为何这样说?
如果使用for循环索引的方式遍历,删除掉一个元素之后,集合的元素个数已经变化,很容易出错。例如
而iterator
的remove()
方法则不会出错,因为通过调用hasNext()
和next()
方法,对指针控制已经处理得比较完善。
二、为什么需要iterator接口
首先,我们知道iterator
接口是为了定义遍历集合的规范,也是一种抽象,把在不同集合的遍历方式抽象出来,这样遍历的时候,就不需要知道不同集合的内部结构。
为什么需要抽象?
假设没有iterator
接口,我们知道,遍历的时候只能通过索引,比如
这样一来,耦合程度比较高,如果使用的数据结构变了,就要换一种写法,不利于维护已有的代码。如果没有iterator
,那么客户端需要维护指针,相当于下放了权限,会造成一定程度的混乱。抽象则是把遍历功能抽取出来,交给iterator
处理,客户端处理集合的时候,交给更“专业”的它,it do it well.
三、iterator接口相关接口
3.1 ListIterator
ListIterator
继承于Iterator
接口,功能更强大,只能用于访问各种List
类型,使用List
类型的对象list
,调用listIterator()
方法可以获取到一个指向list
开头的ListIterator
从上面图片接口看,这个接口具有访问下一个元素,判断是否有下一个元素,是否有前面一个元素,判断是否有前一个元素,获取下一个元素的索引,获取上一个元素的索引,移除元素,修改元素,增加元素等功能。和普通的Iterator
不一样的是,ListIterator
的访问指针可以向前或者向后移动,也就是双向移动。
测试代码如下:
输出如下:
如果点开ArrayList
的源码,看到与ListIterator
相关的部分,我们会发现其实ArrayList
在底层实现了一个内部类ListItr
,继承了Itr
,实现了ListIterator
接口。这个Itr
其实就是实现了Iterator
,实现了基本的List迭代器功能,而这个ListItr
则是增强版的专门为List
实现的迭代器。里面使用cursor
作为当前的指针(索引),所有函数功能都是操作这个指针实现。
我们可以看到,在上面方法中,有很多校验,比如checkForComodification()
,意为检查是否被修改,list中的元素修改有可能导致数组越界。
3.2 SpitIterator
准确地来说,SpitIterator
和Iterator
并没有什么关系,只是两个功能上有类似。SpitIterator
主要是定义类将集合分割成多个集合,方便并行计算。
3.2.1 SpitIterator源码方法解析
使用的方法例子如下:
- tryAdvance() 一个一个元素进行遍历
- forEachRemaining() 顺序地分块遍历
- trySplit()进行分区形成另外的 Spliterator,使用在并行操作中,分出来的是前面一半,就是不断把前面一部分分出来
结果如下:
还有一些其他的用法在这里就不列举了,主要是trySplit()之后,可以用于多线程遍历。理想的时候,可以平均分成两半,有利于并行计算,但是不是一定平分的。
3.2.2 SpitIterator里面哪些特征常量有什么用呢?
spliterator
可以将其实现特征表示为同一接口中定义的一组常量。也就是我们见到的ORDERED
,DISTINCT
,SORTED
,SIZED
之类的,这个意思是每一个实现类,都有自己的实现方式,实现方式不同,实现特征也不一样,比如ArrayList
实现特征是ORDERED
,SIZED
和SUBSIZED
,这个我们可以通过
characteristics()
and hasCharacteristics()
来判断。例如:
输出的结果是
输出结果中的16464和其他的怎么挂钩的呢?其实我们发现上面的hasCharacteristics()
方法中,实现是return (characteristics() & characteristics) == characteristics;
,不难看出,这些状态是根据与运算来计算出来的。上面的结果也表明ArrayList
有ORDERED
,SIZED
和SUBSIZED
这几个特征。
如果是HashSet
则特征是DISTINCT
和SIZED
。
四、 iterator在集合中的实现例子
iterator
只是一个接口,相当于一个规范,所有的子类或者继承类实现的时候理论上应该遵守,但是不一样的继承类/子类会有不一样的实现。
4.1 iterator在ArrayList的实现
iterator
只是一个接口,一个规范,虽然里面有个别方法有默认实现,但是最重要也最丰富的的,是它在子类中的实现与拓展,现在来看在ArrayList
中的实现。ArrayList
并没有直接去实现iterator
接口,而是通过内部类的方式来操作,内部类为Itr
,
从上面的源码可以看到,很多关于被修改的检查,集合会追踪修改(增删改)的次数(modCount 又称版本号),每一个迭代器会单独立维护一个计数器,在每次操作(增删改),检查版本号是否发生改变,如果改变,就会抛出ConcurrentModificationException() 异常,这是一种安全保护机制。
安全检查,快速失败机制实现主要和变量modCount
,expectedModCount
,以及一个checkForComodification()
方法有关,也就是expectedModCount
是内部类的修改次数,从字面意思看是指理论上期待的修改次数,modCount
是外部类的修改次数,创建的时候,会将modCount
赋值给expectedModCount
,两者保持一致,如果在迭代的过程中,外部类的modCount
对不上expectedModCount
,n那么就会抛出ConcurrentModificationException
异常。
4.2 iterator在HashMap的实现
首先,HashMap
里面定义了一个HashIterator
,为什么这样做呢?因为HashMap
存储结构的特殊性,里面有Entry<key,value>,所以遍历就有三种情况,一个是Key,一个是Value,另一个就是Entry,这三个的迭代遍历都有相似性,所以这里根据抽象原则,定义了一个Hash迭代器。
之后分别定义KeyIterator
,ValueIterator
,EntryIterator
,继承于HashIterator
,
五、总结
以上的种种,关于Iterator
,其实就是一个迭代器,可简单地理解为遍历使用,主要功能是指向一个节点,向前或者向后移动,如果数据结构复杂就需要多个迭代器,比如HashMap
,可以避免多个迭代器之间相互影响。每一个迭代器都会有
expectedModCount 和modCount,就是校验这个迭代过程中是否被修改,如果修改了,则会抛出异常。
此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~
__EOF__

本文链接:https://www.cnblogs.com/Damaer/p/13923151.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库