0.参考资料



1.概述

  • 提供一种方法顺序访问一-个聚合对象中的各个元素, 而又不暴露(稳定)该对象的内部表示。一《设计模式》GoF (即: 通过迭代器来隔离算法和容器)
  • 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。通过迭代器来隔离算法和容器

1.1动机

  1. 1. 在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种"透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。
  2. 1. 使用面向对象技术将这种遍历机制抽象为"迭代器对象”, 为“应对变化中的集合对象”提供了一种优雅的方式。
  3. 1. 如果我们的集合元素是用不同的方式实现的,有数组,还有链表等,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。

1.2结构

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629701384434-cd12005a-5673-4c22-8b9e-d21f56c62339.png#clientId=u0933f855-8f9d-4&from=paste&height=217&id=ue077ebb2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=434&originWidth=907&originalType=binary&ratio=1&size=110527&status=done&style=none&taskId=ub14e178b-4323-4399-9d0b-e063641e11f&width=453.5)
  2. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629712915687-6d293ab5-7b32-460f-8698-4e7f3a2faa44.png#clientId=u0933f855-8f9d-4&from=paste&height=210&id=u184428e8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=419&originWidth=650&originalType=binary&ratio=1&size=24011&status=done&style=none&taskId=ud3c67f05-d62f-4d24-a71b-05120b0d911&width=325)
  3. - 角色与职责分析
  4. - Iterator :迭代器接口,是系统提供,含抽象的 hasNext(), next()
  5. - ConcreteIterator: 具体的迭代器类,管理对应的具体数据类型的迭代
  6. - Aggregate:一个统一的聚合接口, 将客户端和具体聚合解耦
  7. - ConcreteAggreage: 具体的聚合持有对象集合,并提供一个方法,返回一个迭代器,该迭代器可以正确遍历(该聚合类的数据)集合
  8. - Client :客户端,通过 Iterator Aggregate 依赖子类

2.要点总结

宏观架构

  1. 1. 迭代抽象: 访问一个聚合对象的内容而无需暴露它的内部表示。
  2. 1. 迭代多态: 为遍历不同的集合结构提供一个统一的接口 ,从而支持同样的算法在不同的集合结构上进行操作。
  3. 1. 迭代器的健壮性考虑: 遍历的同时更改迭代器所在的集合结构,会导致问题。(并发修改异常)

微观代码

  1. 1. 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
  2. 1. 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
  3. 1. 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把维护对象底层数据结构和操作对象集合(如遍历)的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
  4. 1. 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
  5. 1. **缺点**: 每种(相同底层数据结构的)聚合对象都要一个(操作对应数据结构的)迭代器,会生成多个迭代器不好管理类

3.案例

需求

  1. - 在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。如图:
  2. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629712661147-54e4a754-a705-4631-95a0-3cbd3d3e2acb.png#clientId=u0933f855-8f9d-4&from=paste&height=97&id=ue457c3c5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=193&originWidth=385&originalType=binary&ratio=1&size=7487&status=done&style=none&taskId=uc3b67354-6c14-4b78-8284-61c5145bd54&width=192.5)
  3. - 其中. 每个 "学院" 中的 "专业"数据, 可能是不同的数据结构来存储

方案

  1. - 将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的

分析

  1. - 实际上我们的要求是 :在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系, 因此这种方案,不能很好实现的遍历的操作
  2. - 解决方案:=> 迭代器模式

4.使用模式

方案

  1. - 使用迭代器模式, 使得具体的院校聚合一个(处理对应的数据结构的)具体的迭代器.

类图

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629713231632-d6e3ac41-5b71-4a9e-b4da-6d256eb29a49.png#clientId=u0933f855-8f9d-4&from=paste&height=256&id=uf83397b0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=511&originWidth=838&originalType=binary&ratio=1&size=261014&status=done&style=none&taskId=u4e026fd8-1bed-4bda-a3c7-8ef5faf730c&width=419)

代码

5.经典使用


5.1JDK中ArrayList集合

分析

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629714080037-e789533e-21fd-4783-bcfe-5af328e9ff70.png#clientId=u0933f855-8f9d-4&from=paste&height=208&id=uc9c6dc59&margin=%5Bobject%20Object%5D&name=image.png&originHeight=415&originWidth=1167&originalType=binary&ratio=1&size=688731&status=done&style=none&taskId=ubf5b0846-99d2-4d37-aaf9-eaa9ecbba91&width=583.5)

类图

  1. - ![A`O~1FTT[N}HO9PURR6{GYT.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629715175187-34806f28-1375-487c-9572-f802751515b0.png#clientId=uf63510c1-399c-4&from=paste&id=u5932e9e6&margin=%5Bobject%20Object%5D&name=A%60O~1FTT%5BN%7DHO9PURR6%7BGYT.png&originHeight=423&originWidth=628&originalType=binary&ratio=1&size=52045&status=done&style=none&taskId=u43090588-2c06-4f07-979c-2c6af868793)

说明

  1. 1. 内部类Itr 具体迭代器类, 只是依赖方式为: ArrayList 的内部类
  2. 1. List 充当了聚合接口,含有一个抽象的iterator():Iterator 方法--本质是其间接实现了 Iterable 接口
  3. 1. ArrayList 是实现聚合接口List的子类,实现了iterator()
  4. 1. 迭代器模式解决了不同集合(ArrayList ,LinkedList) 统一遍历问题

6.自定义List迭代器

说明

  1. - [ArrayList及其迭代器](#puB6G)

类图

  1. - ![5YJKE%1$`5CCAIC({3_[)[E.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629714754728-69fcc929-30df-4022-963c-8792cdce9cab.png#clientId=u0933f855-8f9d-4&from=paste&height=318&id=u7d1d1483&margin=%5Bobject%20Object%5D&name=5YJKE%251%24%605CCAIC%28%7B3_%5B%29%5BE.png&originHeight=635&originWidth=1062&originalType=binary&ratio=1&size=55376&status=done&style=none&taskId=u2b7f1906-ab48-4281-8926-345a18774cd&width=531)

代码

  1. - 迭代器类: 操作数组的 / 操作链表的
  1. /**
  2. * @description: 模拟ArrayList的(原内部类形式)迭代器. 本质是针对数组的迭代器
  3. */
  4. public class MyArrayListIterator implements Iterator {
  5. // 底层数据
  6. private Object[] elementData;
  7. // 当前下标
  8. private int index = -1;
  9. // 构造器, 需要接受该迭代器对应类型的数据
  10. public MyArrayListIterator(Object[] arr) {
  11. this.elementData = arr;
  12. }
  13. // Iterator接口中, 待实现的 hasNext(), next()
  14. // 在该类中, 是针对 数组 的操作实现
  15. @Override
  16. public boolean hasNext() {
  17. if ( elementData[++index] != null ){
  18. return true;
  19. }
  20. return false;
  21. }
  22. @Override
  23. public Object next() {
  24. return elementData[index];
  25. }
  26. }
  27. /**
  28. * @description: 模拟LinkedList的迭代器.
  29. * 为方便测试, 底层直接用包装好的原生的LinkedList
  30. */
  31. public class MyLinkedListIterator implements Iterator {
  32. // 底层数据
  33. List list;
  34. // 当前下标
  35. int index = -1;
  36. // 构造器, 需要接受该迭代器对应类型的数据
  37. public MyLinkedListIterator(List list){
  38. this.list = list;
  39. }
  40. // Iterator接口中, 待实现的 hasNext(), next()
  41. // 在该类中, 是针对 链表 的操作实现
  42. @Override
  43. public boolean hasNext() {
  44. if (index++ >= list.size()-1){
  45. return false;
  46. }
  47. return true;
  48. }
  49. @Override
  50. public Object next() {
  51. return list.get(index);
  52. }
  53. }
  1. - 简单的 自定义List 及自定义的 ArrayList/LinkedList
  1. /**
  2. * @description: 自定义的抽象基类. 使得方便多态
  3. */
  4. public abstract class MyList implements Iterable{
  5. // 模拟List拥有的方法
  6. protected abstract void add(Object object);
  7. }
  8. // 下面是具体的实现类
  9. /**
  10. * @description: 模拟 ArrayList. 其底层数据结构是数组
  11. */
  12. public class MyArrayList extends MyList implements Iterable {
  13. Object[] elementData; // 底层数组
  14. int size = 0; // 实际元素量
  15. {
  16. // 设置默认容量, 不考虑扩容问题
  17. this.elementData = new Object[512];
  18. }
  19. // 简单的增加方法
  20. @Override
  21. public void add(Object obj){
  22. elementData[size++] = obj;
  23. }
  24. /**
  25. * @description: 返回该类对应的迭代器
  26. */
  27. @Override
  28. public Iterator iterator() {
  29. return new MyArrayListIterator(this.elementData);
  30. }
  31. }
  32. public class MyLinkedList extends MyList implements Iterable {
  33. // 方便起见, 直接使用jdk中包装了链表的LinkedList
  34. List list = new LinkedList();
  35. // 简单的增加方法
  36. @Override
  37. public void add(Object obj){
  38. // 直接用LinkedList现有的, 方便测试
  39. list.add(obj);
  40. }
  41. /**
  42. * @description: 返回该类对应的迭代器
  43. */
  44. @Override
  45. public Iterator iterator() {
  46. return new MyLinkedListIterator(this.list);
  47. }
  48. }
  1. - 测试
  1. public class Client {
  2. public static void main(String[] args) {
  3. MyArrayList myArrayList = new MyArrayList();
  4. genericMethodForAddAndShow(myArrayList);
  5. System.out.println("-----------");
  6. MyLinkedList myLinkedList = new MyLinkedList();
  7. genericMethodForAddAndShow(myLinkedList);
  8. }
  9. /**
  10. * @description: 针对 MyList 的统一操作
  11. * @param: list 传入的容器
  12. */
  13. public static void genericMethodForAddAndShow(MyList list){
  14. {
  15. list.add("test1");
  16. list.add("test2");
  17. list.add("test3");
  18. list.add("test4");
  19. list.add("test5");
  20. list.add("test6");
  21. }
  22. // 通过不同的容器, 获取不同的迭代器.
  23. // 将 底层算法与容器 隔离.暴露一个共通效果(具体实现却不同)的接口
  24. Iterator iterator = list.iterator();
  25. System.out.println(iterator.getClass().getSimpleName());
  26. while (iterator.hasNext()){
  27. Object next = iterator.next();
  28. System.out.println(next);
  29. }
  30. }
  31. }
  1. MyArrayListIterator
  2. test1
  3. test2
  4. test3
  5. test4
  6. test5
  7. test6
  8. -----------
  9. MyLinkedListIterator
  10. test1
  11. test2
  12. test3
  13. test4
  14. test5
  15. test6