欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > Java迭代器【设计模式之迭代器模式】

Java迭代器【设计模式之迭代器模式】

2025/4/4 18:09:20 来源:https://blog.csdn.net/2301_79232523/article/details/146917899  浏览:    关键词:Java迭代器【设计模式之迭代器模式】

目录

一.前言

二.正文

1.我写的类为什么不能使用增强for(迭代器遍历)

2.代码健全性——迭代器常见的两个Exception

1.NoSuchElementException

2.ConcurrentModificationException

三.后言


一.前言

本篇面向对象主要为和我一样的小白,主要是对迭代器模式的浅析和实现,会以大家最常见的ArrayList作为举例也会带着大家去分析一下源码,为了便于理解尽量口语化

本篇采用Kotlin代码,但以Java源码进行解析,因为首先最近实习一直在用Kotlin,其次这两个语言真的太像了,甚至源码都能说很多一样,想让大家可以注意到这门语言。

Kotlin 是一种在 Java 虚拟机(JVM)上运行的静态类型编程语言,由 JetBrains 开发,Kotlin 代码更为简洁,减少了样板代码,能让开发者以更少的代码实现相同功能。

二.正文

1.我写的类为什么不能使用增强for(迭代器遍历)

增强for循环相信大家都写过,如下图:

这样我们可以访问list中的每一个元素,接下来我们定义一个Student类来试试

Student类中有name和age两个属性 

下图可以看到我们对student对象使用增强for失败了,那这是为什么呢,那么,我们就要去ArrayList里面找找原因了,去找找这个for ArrayList实现的上级接口是Collection:

可以看到Collection继承了Iterable接口,我们接近跟进,我们找到了增强for,这段英文的大概意思是实现了这个接口那么对象就可以使用增强for

那好办了,那我就让Student类实现这个接口试试呗,泛型就随便填个String,继承这个接口需要重写iterator方法,这个方法返回Iterator对象,返回的这个其实就是迭代器对象

我们跟进 Iterator接口,可以看到里面有next方法和hasNext方法

而我们又知道增强for循环的另一种写法也就是迭代器遍历(增强for的底层就是调用迭代器遍历),看两个函数的返回类型我们就可以猜到,hasNext函数返回布尔类型用于循环判断条件,next用于拿取元素,那我们先写一个迭代器遍历的代码,然后来简单看看ArrayList是怎么实现这几个方法的

进入iterator方法,可以看到iterator方法返回了一个对象

这个对象是ArrayList的一个内部类,也就是说ArrayList通过调用iterator方法获取迭代器其实是得到了Itr对象,这个内部类实现了Iterator接口,所以重写了next方法和hasNext方法,不必了解太多,后面再分析源码,你只要知道这里的hastNext在判断是否越界,next方法用于返回当前访问到的元素,cursor就是一个变量来表示访问到了第几个元素

那思路就清晰啦,我来给你梳理一下:增强for循环就是迭代器遍历,想要对象可以进行迭代器遍历,就要相应的类实现Iterable接口,表示可迭代的,实现了这个接口就要重写iterator方法,这个方法是用于暴露到外界来获取迭代器对象的,所以我们肯定要有一个类来表示迭代器对象,在这里定义成内部类再合适不过啦,定义的内部类迭代器对象需要实现Iterator接口,重写两个函数来表示迭代器的具体实现逻辑。

这种实现方式将很多信息隐藏在底层,这里就要扯出迭代器模式的概念了,如下:

迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。

 那我们就可以自己给Student实现迭代器功能,比如说来访问Student类的对象的每一个属性的值如下图,我们让Student的iterator方法返回我们定义的一个实现了Iterator接口的内部类StudentIterator,为了不报错,需要重写hasNext和next两个方法

这里的思路很简单啦(代码健全性先暂不考虑,我们先简单实现逻辑,之后慢慢来盘哈),我们定义三个变量,elementArray用来表示外部类的每个属性,这里为数组类型,泛型为Field,这里的Field全类名为java.lang.reflect.Field,看到reflect你可能猜到,这里的elementArray是通过反射拿到的,这里采用反射来拿信息,就不用内外类交互了。arrayLength是属性数组的长度,count是指针,用来表示遍历到第几个元素了,然后再init代码块中初始化变量

 这里注意一下,elementArray表示的是Student类的每个属性名,而不是值,为了接下来的讲解不会让某些点让大家感到疑惑,我这边展示一下反射拿到属性每个值的代码:

接下来我们实现next方法,上图中我们可以看到反射拿取值element.get(student),element我们已经有了,就在数组里,对象怎么拿到?别忘了Student类是返回了一个StudentIterator对象回去,那我们我可以利用构造函数传递呀,则hasNext实现如下两图: 

2.代码健全性——迭代器常见的两个Exception

1.NoSuchElementException

换句话就是数组越界了,有人疑惑了,不是?我hasNext判断了啊,而且next里指针每次就加一,这些代码符合迭代器模式,都在底层没暴露出去,外界修改不了指针的值呀,怎么会越界,你这种想法是建立在调用一次hasNext后只调用一次next,如下图代码:集合内只有5个元素,但调用了多次next

我们可以看到ArrayList源码中就在next中做了判断:

所以我们也做个判断,让我们的StudentIterator代码更加健全: 

2.ConcurrentModificationException

从英文字面上理解就是在遍历过程中对集合进行了修改,当然这里所说的修改是指手动删除添加元素(Iterator其实在内部提供了一个remove方法让我们安全得删除元素),也就是改变集合的长度,写过迭代器的朋友肯定知道这个报错,让我们来看看ArrayList源码中在哪抛的这个错误

跟进如下图,我们这里是浅析就不分析每个变量,下面的代码就是在判断集合长度是否变化了,在这里你肯定很疑惑,为什么要做这个判断?

这个问题我之前也想了一会,我来给一种说法,看如下代码,假如遍历到的元素为5,就添加一个5到集合元素中去,我们知道,集合的底层实现其实就是个数组,假如我们添加元素是将元素添加到最后面,那么这个迭代遍历永远不会结束,假如我们是把元素添加到前面,那每次添加元素意味着数组要移位一次,假如数组很大,时间复杂度是不小的,所以我想这个错误是为了避免这些极端情况

三.后言

好啦,看到这我相信你已经能得心应手得实现一个迭代器啦,也懂了一部分集合源码,ArrayList的源码就不说啦,因为上面所有内容就算讲了一遍了,第二个错误的健全代码也不说啦,我希望你自己去看源码自己实现,也挺简单的,第一次开坑设计模式系列,看大家喜不喜欢这类小白文再考虑要不要实习期间花时间更新吧,有问题欢迎在评论区交流,我也是初学者,共勉

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词