做人就要沉下心来,踏踏实实地去努力。别人说你几句,听着。骂你几句,忍着。没有人会跟父母似得惯着你。低下头勤勤恳恳去做事才是正道,心高气傲只会使你废在半路上。你很年轻,还没有资本指点江山。一己是人,众人是天;谋事在人,成事在天,你必须不断充实自己。你只有足够强大,才不会被别人践踏。
设计模式学习,近期我会把23中设计模式都写成博客,敬请期待~
—2021/1/19
定义
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
什么是迭代器
public interface Iterator<E> {
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
E next();
}
Iterator(迭代器)是一个接口,需要实现两个方法:
- hasNext():有没有下一个元素
- next():当前元素
Iterator(迭代器)常用于数据的遍历,通常使用可能是map集合中使用,其实List集合中也可以使用Iterator(迭代器)来遍历数据.
ArrayList<String> mList = new ArrayList<>();
mList.add("张三");
mList.add("李四");
mList.add("王五");
Iterator<String> iterator = mList.iterator();
//判断有没有下一个元素
while (iterator.hasNext()) {
//输出当前元素
Log.i("迭代器模式",iterator.next());
}
Log图(1.1)
:
ArrayList迭代器模式源码分析
源码:
public Iterator<E> iterator() {
return new Itr();
}
//Itr为内部类
private class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
int cursor; // 要返回的下一个元素的索引
int lastRet = -1; // 返回最后一个元素的索引;如果没有,则返回-1
int expectedModCount = modCount;
public boolean hasNext() {
//当前下标 < ArrayList集合长度 则返回true
return cursor < limit;
}
//获取下一个元素
@SuppressWarnings("unchecked")
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
if (i >= limit)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
ArrayList源码分析图(2.1)
:
在这个实现类中,实现了判断有没有下一个元素(hasNext())以及当前元素是什么(next())
next()分析:
ArrayList源码分析图(2.2)
:
在next()中获取当前elementData数组的值,然后在进行数组长度判断,也从这里看出,ArrayList底层是数组
项目需求分析
假设在一个学校中,需要遍历当前的年级,以及当前年级对应班级的小组,
和组合模式需求是一样的.
区别:
- 组合模式可以以单个元素遍历
- 迭代器模式通过迭代器,可以遍历数组以及集合;
遍历出所有的小组以及年级.
UML分析(3.1)
:
- 红框: 年级
- 篮框: 小组迭代器
类图看起来稍微复杂一些,不要紧,先一步一步来代码实现,代码实现之后,在回头来看这个UML类图就清晰了~
代码实现
Group01terator第一组(假设是以数组方式存放):
public class Group01terator implements Iterator {
//元素数组
Group[] groups;
//下标
int index = 0;
public Group01terator(Group[] groups) {
this.groups = groups;
}
/**
* 判断有无下一个元素
*
* @return true有下一个
*/
@Override
public boolean hasNext() {
//当前下标 > 元素长度 || 当前元素为null 说明没有下一个元素
if (index >= groups.length || groups[index] == null) {
return false;
} else {
//若有下一个元素 当前下标++
return true;
}
}
/**
* 返回当前元素
* @return 当前数组对象
*/
@Override
public Object next() {
return groups[index++];
}
}
Group02terator第二组(假设是以集合方式存放):
public class Group02terator implements Iterator {
//元素数组
List<Group> list ;
//下标
int index = 0;
public Group02terator(List<Group> list) {
this.list = list;
}
/**
* 判断有无下一个元素
*
* @return true有下一个
*/
@Override
public boolean hasNext() {
//当前下标 > 元素长度
if (index > list.size() - 1) {
return false;
} else {
//若有下一个元素 当前下标++
return true;
}
}
/**
* 返回当前元素
* @return 当前数组对象
*/
@Override
public Object next() {
return list.get(index++);
}
}
Classes 班级接口:
public interface Classes {
/**
* @return 当前具体班级
*/
public String showClasses();
/**
* @param name 具体小组
*/
public void printGroup(String name);
/**
* @return 当前迭代器
*/
public Iterator createIterator();
}
Classes01一年级:
public class Classes01 implements Classes {
Group[] group;
/**
* 数组下标
*/
int index = 0;
public Classes01() {
group= new Group[5];
printGroup("雄鹰组");
printGroup("卧龙组");
printGroup("凤雏组");
printGroup("神龟组");
}
@Override
public String showClasses() {
return "一年级";
}
/**
* @param name 学生姓名
*/
@Override
public void printGroup(String name) {
group[index++] = new Group(name);
}
@Override
public Iterator createIterator() {
return new Group01terator(group);
}
}
Classes01重写方法介绍:
- showClasses(): 输出当前的班级
- printGroup(): 输出当前的小组 (在有参构造中初始化了4个)
- createIterator(group[]):将初始化的参数,传递给刚刚写好的Group01terator让他迭代,从而实现数据遍历
Classes02二年级:
public class Classes02 implements Classes {
List<Group> mlist = new ArrayList<Group>();
/**
* 数组下标
*/
int index = 0;
public Classes02() {
printGroup("干饭人组");
printGroup("Giao组");
printGroup("神枪手组");
printGroup("重案八组");
}
@Override
public String showClasses() {
return "二年级";
}
/**
* @param name 学生姓名
*/
@Override
public void printGroup(String name) {
mlist.add(new Group(name));
}
@Override
public Iterator createIterator() {
return new Group02terator(mlist);
}
}
Classes02和Classes01重写方法是一样的,就不重复说了!
OutPut输出结果(为了遵守迪米特原则(最少知道原则)):
public class OutPut {
List<Classes> mlist;
public OutPut(List<Classes> mlist) {
this.mlist = mlist;
}
public void printClasses() {
Iterator<Classes> iterator = mlist.iterator();
while (iterator.hasNext()) {
Classes next = (Classes) iterator.next();
Log.i("迭代器模式"," ========"+next.showClasses()+" ========");
printGroup(next.createIterator());
}
}
/**
* 输出小组
* @param iterator 当前实现Iterator的类
*/
public void printGroup(Iterator iterator){
while (iterator.hasNext()) {
Group next = (Group) iterator.next();
Log.i("迭代器模式", next.getName());
}
}
}
这里只需要在有参构造中传递实现Classes接口的具体实现类,即可完成对所有数据的遍历.
Log(1.2)
:
角色分析
- Iterator:迭代器,用来遍历元素
- Group01terator / Group02terator 迭代器具体实现
- Classes:统一的聚合接口,将客户端和具体聚合解耦
- Classes01 / Classes02:统一聚合接口实现
- OutPut:分离客户端与具体代码实现,为了遵守迪米特原则(最少知道原则)
总结:
优点:
- 支持不同对象遍历
- 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码
- 在同一个聚合上可以有多个遍历
- 不会暴露原有代码实现代码的遍历
缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
原创不易,您的点赞就是对我最大的支持,记得点赞哦~