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的源码就不说啦,因为上面所有内容就算讲了一遍了,第二个错误的健全代码也不说啦,我希望你自己去看源码自己实现,也挺简单的,第一次开坑设计模式系列,看大家喜不喜欢这类小白文再考虑要不要实习期间花时间更新吧,有问题欢迎在评论区交流,我也是初学者,共勉

相关推荐
程序员敲代码吗5 小时前
如何通过命令行启动COMSOL的参数化、批处理和集群扫描
java·c#·bash
MX_93595 小时前
Spring的bean工厂后处理器和Bean后处理器
java·后端·spring
蒟蒻的贤5 小时前
yolo12结构学习
学习
市场部需要一个软件开发岗位5 小时前
JAVA开发常见安全问题:纵向越权
java·数据库·安全
历程里程碑5 小时前
普通数组----合并区间
java·数据结构·python·算法·leetcode·职场和发展·tornado
程序员泠零澪回家种桔子6 小时前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构
●VON6 小时前
CANN推理引擎:从云端到边缘的极致加速与部署实战
学习·react native
CodeCaptain6 小时前
nacos-2.3.2-OEM与nacos3.1.x的差异分析
java·经验分享·nacos·springcloud
笔画人生6 小时前
深度解析 CANN 项目:以 `ops-transformer` 为例探索高性能 AI 算子库
学习·开源
AI视觉网奇6 小时前
3d数字人 ue blender 绑定衣服对齐 2026
学习·ue5