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 ...
随机推荐
- 数据可视化之DAX篇(三) 认识DAX中的表函数和值函数
https://zhuanlan.zhihu.com/p/64421003 学习 DAX 的过程中,会遇到各种坑,刚开始甚至无法写出一个正确的度量值,总是提示错误.其实很多原因都是不理解 DAX 函数 ...
- 数据可视化之分析篇(五)如何使用Power BI计算新客户数量?
https://zhuanlan.zhihu.com/p/65119988 每个企业的经营活动都是围绕着客户而开展的,在服务好老客户的同时,不断开拓新客户是每个企业的经营目标之一. 开拓新客户必然要付 ...
- Python读取文件基本方法
在日常开发过程中,经常遇到需要读取配置文件,这边就涉及到一个文本读取的方法. 这篇文章主要以Python读取文本的基础方法为本,添加读取整篇文本返回字符串,读取键值对返回字典,以及读取各个项返回列表的 ...
- Python 图像处理 OpenCV (14):图像金字塔
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- Iphone上对于动态生成的html元素绑定点击事件$(document).click()失效解决办法
在Iphone上,新生成的DOM元素不支持$(document).click的绑定方法,该怎么办呢? 百度了N久都没找到解决办法,在快要走投无路之时,试了试Google,我去,还真找到了,歪国人就是牛 ...
- 不会吧,有人用了两年Spring, 居然不知道包扫描是怎么实现的
全栈的自我修养: 0004 Java 包扫描实现和应用(File篇) I may not be able to change the past, but I can learn from it. 我也 ...
- 一位Google高管审查了20,000+简历,他发现了这5个致命的错误
工作与生活的平衡 下班划水摸鱼时间,我比较喜欢浏览一下各类新闻网页,比如说ins,这不,我就在ins上看到了这样的一篇文章,内容很简单,就是简历,但是就是这样一份简历,却让这位Google高管震惊不已 ...
- 【week1错题集】
day9[2.f] # day9 题2.f ''' 有如下文件,t1.txt,里面的内容为: 葫芦娃,葫芦娃, 一根藤上七个瓜 风吹雨打,都不怕, 啦啦啦啦. 以r模式打开文件,从‘风吹雨打..... ...
- DirectX11 With Windows SDK--35 粒子系统
前言 在这一章中,我们主要关注的是如何模拟一系列粒子,并控制它们运动.这些粒子的行为都是类似的,但它们也带有一定的随机性.这一堆粒子的几何我们叫它为粒子系统,它可以被用于模拟一些比较现象,如:火焰.雨 ...
- DQL_MySQL
4.DQL(查询数据){SUPER 重点} 4.1DQL (Data Query Language : 数据查询语言) -所有的查询操作: Select 数据库中最核心的语言 create data ...