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

相关推荐
技术干货贩卖机2 分钟前
MATLAB之数据分析图系列 三
学习·matlab·数据挖掘·数据分析·可视化
green5+123 分钟前
LeetCode18四数之和
java·开发语言·算法
lzjava202435 分钟前
Redis数据结构之Set
java·数据结构·redis
Excuse_lighttime1 小时前
JAVA单例模式
java·开发语言·单例模式
每次的天空1 小时前
Android学习总结之应用启动流程(从点击图标到界面显示)
android·学习
·醉挽清风·1 小时前
学习笔记—C++—入门基础()
c语言·开发语言·c++·笔记·学习·算法
wjm0410061 小时前
C++的四种类型转换
java·开发语言·c++
知识分享小能手1 小时前
CSS3学习教程,从入门到精通, 化妆品网站 HTML5 + CSS3 完整项目(26)
前端·javascript·css·学习·css3·html5·媒体
沙子可可2 小时前
深入学习Pytorch:第一章-初步认知
人工智能·pytorch·深度学习·学习
小园子的小菜2 小时前
深入探究 RocketMQ 中的 Broker2Client 组件
java·rocketmq·java-rocketmq