前言介绍
接下里介绍的是Java 的设计模式之一:迭代器模式
咱们还是以一个问题进行开展,引入迭代器模式
编写程序展现一个学校院系构造:需要是这样,要在一个页面中展现出学校的院系组成,一个学校有多个学院, 一个学院有多个系。如图
咱们之前用组合模式解决过这个问题,然而咱们当初要以遍历的角度去思考
怎么遍历他们?
一、传统形式解决问题
比如说目前
计算机学院采纳的是数组的形式存储、信息学院采纳汇合存储
那么咱们怎么去遍历他们?
解决方案:=> 迭代器模式
二、什么是迭代器模式
迭代器模式(Iterator Pattern)是罕用的设计模式,属于行为型模式
如果咱们的汇合元素是用不同的形式实现
的,有数组,还有汇合类,或者还有其余形式,当客户端要遍历这些汇合元素的时候就要应用多种遍历形式,而且还会裸露元素的内部结构,能够思考应用迭代器模式解决。
迭代器模式,提供一种遍历汇合元素的对立接口,用统一的办法遍历汇合元素,不须要晓得汇合对象的底层示意
,即:不裸露其外部的构造
迭代器原理类图剖析
Iterator :迭代器接口由零碎提供,含意 hasNext, next, remove
ConcreteIterator :具体的迭代器类,治理迭代
Aggregate :一个对立的聚合接口将客户端和具体聚合解耦
ConcreteAggreage : 具体的聚合持有对象汇合, 并提供一个办法,返回一个迭代器, 该迭代器能够正确遍历汇合
Client :客户端, 通过 Iterator 和 Aggregate 依赖子类
三、应用迭代器模式解决问题
依据咱们的思路,因为学院是蕴含系的,所以咱们须要先创立系这个类
//系class Department { private String name;//名称 private String desc;//形容 public Department(String name, String desc) { super(); this.name = name; this.desc = desc; } public String getName() {return name;} public void setName(String name) {this.name = name;} public String getDesc() {return desc;} public void setDesc(String desc) {this.desc = desc;}}
假如咱们计算机学院采纳的是数组的形式存储相干的计算机系
那么咱们依据思路创立计算机学院实现迭代器接口的实现类
class ComputerCollegeIterator implements Iterator { //这里Department是以数组的形式寄存 Department[] departments; //遍历的地位 int position = 0; public ComputerCollegeIterator(Department[] departments) { this.departments = departments; } //判断是否还有下一个元素 @Override public boolean hasNext() { if (position >= departments.length || departments[position] == null) { return false; } else { return true; } } @Override public Object next() { Department department = departments[position]; position += 1; return department; } //删除的办法,默认空实现 @Override public void remove() {}}
假如咱们信息学院采纳的是汇合的形式存储相干的信息系
那么咱们依据思路创立信息学院实现迭代器接口的实现类
class InfoColleageIterator implements Iterator { // 信息工程学院是以 List 形式寄存系 List<Department> departmentList; //索引 int index = -1; public InfoColleageIterator(List<Department> departmentList) { this.departmentList = departmentList; } //判断 list 中还有没有下一个元素 @Override public boolean hasNext() { if (index >= departmentList.size() - 1) { return false; } else { index += 1; return true; } } @Override public Object next() { return departmentList.get(index); } // 空 实 现 remove @Override public void remove() {}}
接下来咱们依据思路创立返回迭代器的接口
interface College { //学院的名称 public String getName(); //减少系的办法 public void addDepartment(String name, String desc); //返回一个迭代器,遍历 public Iterator createIterator();}
咱们依据思路首先创立于计算机学院对应的返回迭代器实现类
class ComputerCollege implements College { //这里Department是以数组的形式寄存 Department[] departments; // 保留以后数组的对象个数 int numOfDepartment = 0; @Override public String getName() { return "计算机学院"; } @Override public void addDepartment(String name, String desc) { //依据传入的信息创立系 Department department = new Department(name, desc); departments[numOfDepartment] = department; numOfDepartment += 1; } @Override public Iterator createIterator() { return new ComputerCollegeIterator(departments); }}
咱们再依据思路创立于信息学院对应的返回迭代器实现类
class InfoCollege implements College { //汇合的形式存储 List<Department> departmentList; @Override public String getName() { return "信息工程学院"; } @Override public void addDepartment(String name, String desc) { Department department = new Department(name, desc); departmentList.add(department); } @Override public Iterator createIterator() { return new InfoColleageIterator(departmentList); }}
计算机学院应用数组的形式存储、信息学院应用汇合的形式存储
这时咱们创立一个输入类,通过迭代器的形式输入所有学院
class OutPutImpl { //学院汇合 List<College> collegeList; public OutPutImpl(List<College> collegeList) { this.collegeList = collegeList; } //遍历所有学院,而后调用 printDepartment 输入各个学院的系 public void printCollege() { //从 collegeList 取出所有学院, Java 中的 List 曾经实现Iterator Iterator<College> iterator = collegeList.iterator(); while (iterator.hasNext()) { //取出一个学院 College college = iterator.next(); System.out.println("=== " + college.getName() + "====="); printDepartment(college.createIterator()); //失去对应迭代器 } } //输入 学院输入 系 public void printDepartment(Iterator iterator) { while (iterator.hasNext()) { Department d = (Department) iterator.next(); System.out.println(d.getName()); } }}
接下来咱们应用demo 一起领会看看,是怎么将两组不同存储形式遍历的
public static void main(String[] args) { //创立存储迭代类 List<College> collegeList = new ArrayList<College>(); //创立返回对应计算机学院迭代器 ComputerCollege computerCollege = new ComputerCollege(); //创立存储计算机学院的里的系 computerCollege.departments = new Department[5]; //将计算机学院里的系,存储到迭代器里 computerCollege.addDepartment("Java 业余", " Java 业余 "); computerCollege.addDepartment("PHP 业余", " PHP 业余 "); computerCollege.addDepartment("大数据业余", " 大数据业余 "); //创立返回对应信息学院对应迭代器 InfoCollege infoCollege = new InfoCollege(); //创立存储计算机学院的里的系 infoCollege.departmentList = new ArrayList<Department>(); //将信息学院里的系,存储到迭代器里 infoCollege.addDepartment("信息安全业余", " 信息安全业余 "); infoCollege.addDepartment("网络安全业余", " 网络安全业余 "); infoCollege.addDepartment("服务器平安业余", " 服务器平安业余 "); //将两个学院的迭代器增加进汇合中 collegeList.add(computerCollege); collegeList.add(infoCollege); //将汇合交给帮忙输入类进行输入 OutPutImpl outPutImpl = new OutPutImpl(collegeList); outPutImpl.printCollege();}运行后果如下:=== 计算机学院=====Java 业余PHP 业余大数据业余=== 信息工程学院=====信息安全业余网络安全业余服务器平安业余
四、迭代器在源码中的应用
咱们先应用一个示例,来看看ArrayList的应用与输入
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Jack"); Iterator<String> integer = list.iterator(); while(integer.hasNext()){ System.out.println(integer.next()); }}运行后果如下:Jack
诶,为什么ArrayList能够应用Iterator迭代器呢?
咱们一起进ArrayList的源码里去看看,发现它实现了List汇合的接口
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ //....省略关键性的代码}
那么List汇合这个接口做了什么事件?咱们也进去看看
public interface List<E> extends Collection<E> { int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); //省略其余关键性代码.....}
诶,咱们发现List汇合里有一个iterator办法
那么ArrayList 有没有对着这个办法实现呢?咱们一起进去找找看看
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ public Iterator<E> iterator() { return new 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; Itr() {} 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(); } } //省略其余关键性代码....}
咱们这样就发现了,一下就思路就苏醒了,请看类图剖析
Itr是ArrayList的一个外部类,自身应用ArrayList里的elementData
因而与原先迭代模式不一样,它是间接应用
五、迭代器模式的注意事项和细节
Ø 长处
提供一个对立的办法遍历对象,客户不必再思考聚合的类型,应用一种办法就能够遍历对象了。
暗藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会晓得聚合的具体组成。
提供了一种设计思维,就是一个类应该只有一个引起变动的起因(叫做繁多责任准则)。
在聚合类中,咱们把迭代器离开,就是要把治理对象汇合和遍历对象汇合的责任离开,这样一来汇合扭转的话,只影响到聚合对象。
而如果遍历形式扭转的话,只影响到了迭代器。
当要展现一组类似对象,或者遍历一组雷同对象时应用, 适宜应用迭代器模式
Ø 毛病
每个聚合对象都要一个迭代器,会生成多个迭代器不好治理类
参考资料
尚硅谷:设计模式(韩顺平老师):迭代器模式
Refactoring.Guru:《深刻设计模式》