文章目录
23种设计模式
#迭代器模式
1.迭代器模式目的
迭代器模式主要实现行为的分离。
实现遍历行为的分离。
2.迭代器模式实现
2.1一些名词
迭代器接口:一个接口,定义hasNext和next方法。
容器接口:一个接口,定义iterator方法。
迭代器:实现了迭代器接口的类。
容器:实现了容器接口的类。
hasNext:判断next方法是否可用
next:返回当前对象,并指向下一个
iterator:返回当前容器的迭代器
remove:删除当前元素
迭代器:进行遍历行为的类
容器:存放元素的类
###2.2接口的定义
迭代器接口
容器接口
2.3实现类定义
迭代器
容器
2.4 实体类定义
元素
2.5 测试类定义
测试
2.6 测试结果
3.迭代器模式扩展
###3.1迭代器模式的接口可以使用jdk的接口
java中util包中的
一般remove方法很少被用到,主要还是前两个方法。
所以呢,要实现迭代器需要实现迭代器的接口
至于容器的接口
java.lang包
所以,如果你只需要迭代器最普通的实现,那么只需要在对应的类上实现对应的接口就行。
3.2例子
3.2.1学校(容器)
3.2.2学生(实体)
3.2.3学校的迭代器
3.2.4测试代码
3.3实现自己的集合
实现自己需要用到的数据存储集合:
一般常用的库帮助我们写了非常多的方法,常用的List,set,Map…等等,有好多都有实现了迭代器,所以我们能够使用这些集合的非常方便的遍历数据。
4.常见的问题
4.1在遍历的过程中删除
普通情况下,我们可能不使用迭代器进行遍历,就像这样:
就for循环来说,最常用可能就这两种,其他的也有,但是比较少用,普通for在基本类型数组中较多,增强for循环在复杂类型数组及存储集合中较多。
在遍历的过程中删除有一个比较麻烦的事情:
使用普通for循环,因为是基本数组,删除其中一个元素,意味着需要把后面的元素依次前移(有序)或者用最后一个元素替换删除元素(无序)然后删除最后一个元素,但是不管怎么删除,因为基本类型数组,可能在一开始就限定了大小,所以,删除需要复制到新数组,需要实现复制方法。
使用增强for循环,是复杂类型数组,一般jdk实现了对这些集合的基本操作(增删取。。。。)。所以删除比较方便,可能调用个方法就行。
但是在增强for循环中直接删除会发生异常:
试验代码:
在上面的例子中修改:
在School.java中添加一个方法:
运行结果:
查看是AbstractList.java:372行抛出的异常:
代码如下:
其中涉及到两个参数:modCount和expectedModCount
这两个参数的定义如下:
看注释猜到这个modCount应该是记录修改次数的变量。
这是另一个参数:
差不多就是并发修改标识。
总之就是这两个值相同才是正确的,不相同表示发生错误。
这是ArrayList的remove方法:
发现在remove方法中并没有对上述两个值的改变,但是有一个方法:
这个方法在remove方法中调用。
在增加的方法中:
add方法中调用了这个方法:
对于一个数组来说,最可能改变modCount的地方就是这两个方法。
举个例子,对于50个元素的arrayList的modCount的值是怎么变化的。
首先新建一个arrayList,modCount=0(初值是0)
然后添加一个元素,modCount加1
当50个元素增加完,modCount=50,expectedmodCount=50
然后删除一个元素时,
modCount加1,modCount=51
然后对删除元素后面的元素进行向前拷贝:
拷贝的方法:
这个方法的原型:
看到原型方法就很清楚这个方法的参数:
源数据,源数据开始下标
目标数据,目标数据开始下标
长度。
所以,在n个元素中删除第i个
参数为:
data,i+1,data,i,n-i-1(这里多减一是因为从0开始)
可以看到,在remove方法中删除元素,导致modCount值+1,且对元素进行重新拷贝。
但是
expectedmodCount的值没有发生改变。
然后我们取出下一个元素的时候,调用了获取元素的方法:
这个应该是增强for循环的一种实现方式,有可能是重载了冒号这个运算符,类似Java中字符串可以用加号连接一样。(我是通过加断点的方式得出调用这个方法获取下一个元素)
其中有一个检测的方法:
当modCount和expectedmodCount值不同,抛出异常。
那么使用迭代器呢?
测试方案:去掉学生age%10==4的学生
在测试方法中进行修改,修改为以下:
执行结果:
那为什么使用迭代器就是安全的?
首先在调试的时候有一个文件AbstractList$Itr这个文件:
使用迭代器删除,我看其他讲这个的文章说的大概意思就是迭代器创建了一个副本(这个可以理解,我们实现迭代器的时候有一个参数:private School school;)所以呢,在副本中进行操作是因为我们在迭代器中实现了方法。而且使用迭代器的删除使用的是这个remove方法,在方法中对expectedmodCount值进行更新,所以不会报错。(还有网上解释说有其他的变量,我暂时还没有搞懂,所以这只是我的一个猜测)
4.2遍历的并发安全性
后续添加。
(有问题欢迎评论)