1、迭代器 Iterator模式 一个一个遍历 行为型设计模式
1、Iterator模式
迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器(container,例如链表或者阵列)上遍访的接口,设计人员无需关心容器的内容。
Iterator模式 - 一个一个遍历,我们将学习从含有多个元素的集合中将各个元素逐一取出来的iterator模式。
导学
对于数组我们使用的是下标来进行处理的:
1 int array[] = new int[3];
2 for (int i = 0; i < array.length; i++) {
3 System.out.println(array[i]);
4 }
对ArrayList的处理
1 List<String> list = new ArrayList<String>();
2 for(int i = 0 ; i < list.size() ; i++){
3 String string = list.get(i);
4 }
将这里的i的作用抽象化,通用化后形成的模式,在设计模式中称为iterator模式。
Iterator模式用于在数据集合按照顺序遍历集合。英语单词iterate有反复做某件事情的意思,汉语称为“迭代器”。
2、实例程序
这段程序的作用是将书(Book)放置到书架(BookShelf)中,并将书的名字按照顺序显示出来.
类和接口的意义:
Aggregate: 表示集合的接口
Iterator: 遍历集合的接口
BookShelf: 表示书架的类
BookShelfIterator: 遍历书架的类
Book: 表示书的类
2.1 Aggregate接口
package cn.design.iterator; /**
* @author lin
* @version 1.0
* @date 2020-07-13 14:16
* @Description 表示集合的接口
*/
public interface Aggregate {
/**
* 在Aggregate接口中声明的方法只有一个一-iterator 方法。该方法会生成-一个用于遍历集合的迭代器。
* 想要遍历集合中的元素时,可以调用iterator方法来生成一一个实现了Iterator接口的类的实例。
*/
public abstract Iterator iterator();
}
2.2 Iterator接口
package cn.design.iterator; /**
* @author lin
* @version 1.0
* @date 2020-07-13 14:30
* @Description TODO
*/
public interface Iterator { /**
* 判断是否存在下一-个元素
* 当集合中存在下一个元素
* 时,该方法返回true;当集合中不存在下一个元素,即已经遍历至集合末尾时,该方法返回
* false。hasNext 方法主要用于循环终止条件。
*
* @return
*/
public abstract boolean hasNext(); /**
* 取下一个元素
* 该方法返回的是集合
* 中的一一个元素。但是,next方法的作用并非仅仅如此。为了能够在下次调用next方法时正确地返
* 回下一个元素,该方法中还隐含着将迭代器移动至下一个元素的处理。说“隐含”,是因为
* Iterator接口只知道方法名。想要知道next方法中到底进行了什么样的处理,还需要看一下实
* 现了Iterator接口的类( BookShelfIterator)。这样,我们才能看懂next方法的作用。
*
* @return object
*/
public abstract Object next();
}
2.3 Book类
package cn.design.iterator; /**
* @author lin
* @version 1.0
* @date 2020-07-13 14:33
* @Description TODO
*/
public class Book {
private String name; public Book(String name) {
this.name = name;
} public Book() {
} public void setName(String name) {
this.name = name;
} public String getName() {
return name;
} @Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
'}';
}
}
2.4 BookShelf类
package cn.design.iterator; /**
* @author lin
* @version 1.0
* @date 2020-07-13 19:00
* @Description TODO
*/
public class BookShelf implements Aggregate {
/**
* 这个书架中定义了books字段,它是Book类型的数组。该数组的大小( maxsize )在生成
* BookShelf的实例时就被指定了。之所以将books字段的可见性设置为private,是为了防止.
* 外部不小心改变了该字段的值。
*/
private Book[] books;
private int last = 0; public BookShelf(int maxSize) {
this.books = new Book[maxSize];
} public Book getBookAt(int index) {
return books[index];
} public void appendBook(Book book) {
this.books[last] = book;
last++;
} public int getLength() {
return last;
} /**
* BookShelf类对应的Iterator。当外部想要遍历书架时,就会调用这个方法。
*
* @return Iterator 实现类
*/
@Override
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
2.5 BookShelfIterator 类
package cn.design.iterator; /**
* @author lin
* @version 1.0
* @date 2020-07-13 19:03
* @Description TODO
*/
public class BookShelfIterator implements Iterator {
/**
* bookShelf字段表示BookShelfIterator所要遍历的书架。
*/
private BookShelf bookShelf;
/**
* index字段表示迭代器当前所指向的书的下标。
*/
private int index; /**
* @param bookShelf
*/ /**
* 构造函数会将接收到的BookShelf的实例保存在bookShelf字段中,并将index初始化为0。
*
* @param bookShelf
*/
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
} /**
* hasNext方法是Iterator接口中所声明的方法。该方法将会判断书架中还有没有下一-本书, .
* 如果有就返回true,如果没有就返回false。而要知道书架中有没有下一本书,可以通过比较
* index和书架中书的总册数( bookShelf . getLength ()的返回值)来判断。
*
* @return
*/
@Override
public boolean hasNext() {
return index < bookShelf.getLength();
} /**
* next方法会返回迭代器当前所指向的书( Book的实例),并让迭代器指向下一-本书。它也是
* Iterator接口中所声明的方法。next方法稍微有些复杂,它首先取出book变量作为返回值,
* 然后让index指向后面- -本书。
* 如果与本章开头的for语句来对比,这里的“让index指向后面一-本书”的处理相当于其中
* 的i++,它让循环变量指向下一个元素。
*
* @return
*/
@Override
public Object next() {
Book bookAt = bookShelf.getBookAt(index);
index++;
return bookAt;
}
}
2.6 TestMain测试类
package cn.design.iterator; import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.List; /**
* @author lin
* @version 1.0
* @date 2020-07-13 19:05
* @Description TODO
*/
public class TestMain { public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("环游世界80天"));
bookShelf.appendBook(new Book("圣经"));
bookShelf.appendBook(new Book("灰姑娘"));
bookShelf.appendBook(new Book("阿拉丁神灯"));
Iterator it = bookShelf.iterator();
while (it.hasNext()) {
Book book = (Book) it.next();
System.out.println(book.toString());
} // ArrayList<String> list = new ArrayList<>();
// list.add("aaaa");
// list.add("bbbb");
// list.add("cccc");
// list.add("dddd");
// java.util.Iterator<String> it2 = list.iterator();
// while (it2.hasNext()) {
// System.out.println("it2.next() = " + it2.next());
// }
}
}
运行结果如下:
Book{name='环游世界80天'}
Book{name='圣经'}
Book{name='灰姑娘'}
Book{name='阿拉丁神灯'}
通过bookShelf. iterator()得到的it是用于遍历书架的Iterator实例。while部分的条件当然就是it.hasNext()了。只要书架上有书,while 循环就不会停止。然后,程序会通过it.next()一本一本地遍历书架中的书。
3、Iterator模式中的各个角色
读完示例程序,让我们来看看Iterator模式中的登场角色。
1、Iterator (迭代器)
该角色负责定义按顺序逐个遍历元素的接口( API)。在示例程序中,由Iterator接口扮演这个角色,它定义了hasNext和next两个方法。其中,hasNext 方法用于判断是否存在下一个元素,next方法则用于获取该元素。
2、Concretelterator (具体的迭代器)
该角色负责实现Iterator角色所定义的接口( API)。在示例程序中,由BookShelfIterator类扮演这个角色。该角色中包含了遍历集合所必需的信息。在示例程序中,BookShelf类的实例保存在bookShelf字段中,被指向的书的下标保存在index字段中。
3、Aggregate (集合)
该角色负责定义创建Iterator角色的接口( API)。这个接口( API)是-一个方法,会创建出“按顺序访问保存在我内部元素的人”。在示例程序中,由Aggregate接口扮演这个角色,它里面定义了iterator 方法。
4、ConcreteAggregate ( 具体的集合)
该角色负责实现Aggregate角色所定义的接口(API)。它会创建出具体的Iterator角色,即Concretelterator角色。在示例程序中,由BookShelf类扮演这个角色,它实现了iterator 方法。
4、扩展思路的要点
4.1、为何iterator必不可少?
while (it.hasNext()) {
Book book = (Book) it.next();
System.out.println(book.toString());
}
上述只使用了Iterator的hasNext方法和next方法,并没有调用BookShelf的方法。也就是说,这里的while循环并不依赖于BookShelf的实现。
如果编写BookShelf的开发人员决定放弃用数组来管理书本,而是用java.util. vector取而代之,会怎样呢?不管BookShelf如何变化,只要BookShelf的iterator方法能正确地返回Iterator的实例(也就是说,返回的Iterator类的实例没有问题,hasNext 和next方法都可以正常工作),即使不对上面的while循环做任何修改,代码都可以正常工作。
这对于BookShelf的调用者来说真是太方便了。设计模式的作用就是帮助我们编写可复用的类。所谓“可复用”,就是指将类实现为“组件”,当一个组件发生改变时,不需要对其他的组件进行修改或是只需要很小的修改即可应对。
这样也就能理解为什么在示例程序中iterator方法的返回值不是BookShelfIterator类型而是Iterator类型了(代码清单1-6)。这表明,这段程序就是要使用Iterator的方法进行编程,而不是BookShelfIterator的方法。
4.2、难以理解的抽象类和接口
难以理解抽象类和接口的人常常使用ConcreteAggregate角色和ConcreteIterator角色编程,而不使用Aggregate接口和Iterator接口,他们总想用具体的类来解决所有的问题。
但是如果只使用具体的类来解决问题,很容易导致类之间的强耦合,这些类也难以作为组件被再次利用。为了弱化类之间的耦合,进而使得类更加容易作为组件被再次利用,我们需要引入抽象类和接口。
4.3、Java中ArrayList源码
私有的内部类Itr
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; public boolean hasNext() {
return cursor != size;
} @SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
} public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
} @Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList内部:
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
获取到Iterator,就可以使用boolean hasNext();和E next();俩个方法进行遍访操作。
Java源码:
发哥讲上传到码云上:
https://gitee.com/naimaohome/talk_about_fage.git
发哥讲
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注公众号
1、迭代器 Iterator模式 一个一个遍历 行为型设计模式的更多相关文章
- Java 实现迭代器(Iterator)模式
类图 /** * 自己定义集合接口, 相似java.util.Collection * 用于数据存储 * @author stone * */ public interface ICollection ...
- 策略模式 Strategy 政策Policy 行为型 设计模式(二十五)
策略模式 Strategy 与策略相关的常见词汇有:营销策略.折扣策略.教学策略.记忆策略.学习策略.... “策略”意味着分情况讨论,而不是一概而论 面对不同年龄段的人,面对不同的商品,必然将会 ...
- 中介者模式 调停者 Mediator 行为型 设计模式(二十一)
中介者模式(Mediator) 调度.调停 意图 用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散 而且可以独立地改变它们之间的交互. ...
- 设计模式—迭代器Iterator模式
什么是迭代器模式? 让用户通过特定的接口访问容器的数据,不需要了解容器内部的数据结构. 首先我们先模仿集合中ArrayList和LinkedList的实现.一个是基于数组的实现.一个是基于链表的实现, ...
- 设计模式C++描述----20.迭代器(Iterator)模式
一. 举例说明 我们知道,在 STL 里提供 Iterator 来遍历 Vector 或者 List 数据结构. Iterator 模式也正是用来解决对一个聚合对象的遍历问题,将对聚合的遍历封装到一个 ...
- 设计模式——迭代器(Iterator)模式
概述 迭代器模式简单的说(按我目前的理解)就是一个类提供一个对外迭代的接口,方面调用者迭代.这个迭代接口至少包括两个方法:hasNext()--用于判断是否还有下一个,next()--用于取出下一个对 ...
- 迭代器Iterator、for循环遍历、泛型
java.util.Collection接口 是集合的最顶层的接口,定义了集合共性的方法 接口无法直接创建对象,使用多态的方式创建对象 Collection<集合中的数据类型(泛型)> c ...
- 观察者模式 Observer 发布订阅模式 源 监听 行为型 设计模式(二十三)
观察者模式 Observer 意图 定义对象一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖他的对象都得到通知并自动更新. 别名:依赖(Dependents),发布订阅(Publish-Su ...
- Head First 设计模式 —— 10. 迭代器 (Iterator) 模式
思考题 public void printMenu() { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); ArrayList ...
随机推荐
- CMDB01 /paramiko模块、项目概述、项目架构、项目实现
CMDB01 /paramiko模块.项目概述.项目架构.项目实现 目录 CMDB01 /paramiko模块.项目概述.项目架构.项目实现 1. paramiko 2. 基于xshell连接服务器 ...
- A Broken Calculator 最详细的解题报告
题目来源:A Broken Calculator 题目如下(链接有可能无法访问): A Broken Calculator Time limit : 2sec / Stack limit : 256M ...
- Video 自动播放
先说ios ios之前的政策是视频只能在用户主动操作后才能播放,且播放时必须全屏. 随着 iOS 10 的正式发布,Safari 也迎来了大量更新,首先划出重点:1)iOS 10 Safari 支持特 ...
- js常见删除绑定的事件
1. elem.onclick = null / false; //直接解除 例子如下: var div = document.getElemetById('id'); div.onclick = ...
- SpringCloud或SpringBoot+Mybatis-Plus利用AOP+mybatis插件实现数据操作记录及更新对比
引文 本文主要介绍如何使用Spring AOP + mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录.在更新数据时准确记录更新字段 核心:AO ...
- [Qt2D绘图]-04绘制文字&&绘制路径
注:学习自<Qt Creator 快速入门>第三版. 文档中的示例参考 Qt Example推荐:Painter Paths Example和Vector Deformation ...
- patelinux 安装
参考文档:https://china.xilinx.com/support/documentation/sw_manuals/xilinx2017_2/ug1144-petalinux-tools-r ...
- 题解 SP3734 【PERIODNI - Periodni】
考虑用\(DP\)和组合数学来解决. 因为原图像不规则的形状不好处理,所以先用笛卡尔树(性质为小根堆)将其划分成一个一个的矩形. 发现在笛卡尔树上的每个节点都对应一个矩形,矩形高为\(h_x-h_{f ...
- 题解 洛谷 P4177 【[CEOI2008]order】
进行分析后,发现最大收益可以转化为最小代价,那么我们就可以考虑用最小割来解决这道题. 先算出总收益\(sum\),总收益减去最小代价即为答案. 然后考虑如何建图,如何建立最小割的模型. 发现一个任务最 ...
- sql 大小写查询 字符串替换 小写xx 改为大写XX
--sql 大小写查询 select * from 表 where 字段 collate Chinese_PRC_CS_AS='xx' --替换 小写xx 改为大写XX update 表 set ...