设计模式之迭代器模式
Posted 活跃的咸鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之迭代器模式相关的知识,希望对你有一定的参考价值。
迭代器模式的引入
如果要问java中使用最多的一种模式,答案不是单例模式,也不是工厂模式,更不是策略模式,而是迭代器模式,先来看一段代码吧:
public static void print(Collection coll){
Iterator it = coll.iterator();
while(it.hasNext()){
String str = (String)it.next();
System.out.println(str);
}
}
这个方法的作用是循环打印一个字符串集合,里面就用到了迭代器模式,java语言已经完整地实现了迭代器模式,Iterator翻译成汉语就是迭代器的意思。提到迭代器,首先它是与集合相关的,集合也叫聚集、容器等,我们可以将集合看成是一个可以包容对象的容器,例如List,Set,Map,甚至数组都可以叫做集合,而迭代器的作用就是把容器中的对象一个一个地遍历出来。
迭代器模式介绍
基本介绍
- 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
- 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,
或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历
方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。 - 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,
不需要知道集合对象的底层表示,即:不暴露其内部的结构。
原理类图
抽象容器(Aggregate):它用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
具体的聚集类(ConcerateAggregate):实现createIterator()方法返回具体的一个迭代器
具体迭代器(ConcreteIterator):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。
抽象迭代器(Iterator):定义遍历元素所需要的方法,一般来说会有这么几个方法:取得第一个元素的方法first(),取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()),移出当前对象的方法remove()。
迭代器模式示例
下面我们用一个专业管理的例子来实现迭代器模式的演示:
public class Major {
private String name;//专业名称
private String desc;//专业描述
public Major(String name, String desc) {
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;
}
@Override
public String toString() {
return "Project{" +
"name='" + name + '\\'' +
", desc='" + desc + '\\'' +
'}';
}
}
抽象迭代器(Iterator)
public interface MajorIterator {
boolean hasNext();
Major next();
}
具体迭代器(ConcreteIterator):
public class MajorIteratorImpl implements MajorIterator{
private List<Major> list;
private int index=0;
public MajorIteratorImpl(List<Major> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return index<list.size();
}
@Override
public Major next() {
Major major=list.get(index);
index++;
return major;
}
}
抽象容器(Aggregate):
public interface majorAggregate {
void addMajor(Major major);
void removeMajor(Major major);
MajorIterator getMajorIterator();
}
具体的聚集类(ConcerateAggregate)
public class majorAggregateImpl implements majorAggregate{
private List<Major> list;
public majorAggregateImpl() {
}
public majorAggregateImpl(List<Major> list) {
this.list = list;
}
@Override
public void addMajor(Major major) {
this.list.add(major);
}
@Override
public void removeMajor(Major major) {
this.list.remove(major);
}
@Override
public MajorIterator getMajorIterator() {
return new MajorIteratorImpl(list);
}
}
clent
public class Test {
public static void main(String[] args) {
majorAggregate majorAggregate=new majorAggregateImpl(new ArrayList<>());
majorAggregate.addMajor(new Major("软件工程","软件工程专业"));
majorAggregate.addMajor(new Major("计算机科学与技术","计算机科学与技术专业"));
majorAggregate.addMajor(new Major("网络工程","网络工程"));
MajorIterator majorIterator=majorAggregate.getMajorIterator();
while (majorIterator.hasNext()){
System.out.println(majorIterator.next());
}
}
}
Project{name='软件工程', desc='软件工程专业'}
Project{name='计算机科学与技术', desc='计算机科学与技术专业'}
Project{name='网络工程', desc='网络工程'}
迭代器模式jdk源码ArrayList中分析
我们平时使用ArrayList
public class Test {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("abc");
list.add("def");
list.add("xyz");
list.add("jkl");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
类图如下:
角色说明:
- 内部类Itr 充当具体实现迭代器Iterator 的类, 作为ArrayList 内部类
- List 就是充当了聚合接口,含有一个iterator() 方法,返回一个迭代器对象
- ArrayList 是实现聚合接口List 的子类,实现了iterator()
- Iterator 接口系统提供
下面我们来看源码:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
transient Object[] elementData; // non-private to simplify nested class access
private int size;
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
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;
public boolean hasNext() {
return cursor != size;
}
public E next() {
//...
}
public E next() {
//...
}
public void remove() {
//...
}
//...
}
private class ListItr extends Itr implements ListIterator<E> {
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
public E previous() {
//...
}
public void set(E e) {
//...
}
public void add(E e) {
//...
}
//...
}
从 ArrayList 源码中看到了有两个迭代器 Itr 和 ListItr,分别实现 Iterator 和 ListIterator 接口;
先看 ListIterator 源码
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
boolean hasPrevious(); // 返回该迭代器关联的集合是否还有上一个元素
E previous(); // 返回该迭代器的上一个元素
int nextIndex(); // 返回列表中ListIterator所需位置后面元素的索引
int previousIndex(); // 返回列表中ListIterator所需位置前面元素的索引
void remove();
void set(E var1); // 从列表中将next()或previous()返回的最后一个元素更改为指定元素e
void add(E var1);
}
接着是 Iterator 的源码
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
// 备注:JAVA8允许接口方法定义默认实现
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
通过源码我们看出:ListIterator 是一个功能更加强大的迭代器,它继承于 Iterator 接口,只能用于各种List类型的访问。可以通过调用 listIterator() 方法产生一个指向List开始处的 ListIterator, 还可以调用 listIterator(n) 方法创建一个一开始就指向列表索引为n的元素处的 ListIterator。
Iterator 和 ListIterator 主要区别概括如下:
ListIterator 有 add() 方法,可以向List中添加对象,而 Iterator 不能
ListIterator 和 Iterator 都有 hasNext() 和 next() 方法,可以实现顺序向后遍历,但是 ListIterator 有 hasPrevious() 和 previous() 方法,可以实现逆向(顺序向前)遍历。Iterator 就不可以。
ListIterator 可以定位当前的索引位置,nextIndex() 和 previousIndex() 可以实现。Iterator 没有此功能。
都可实现删除对象,但是 ListIterator 可以实现对象的修改,set() 方法可以实现。Iierator 仅能遍历,不能修改。
迭代器模式的注意事项和细节
优点:
- 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以
遍历对象了。 - 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚
合的具体组成。 - 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任
原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集
合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变
的话,只影响到了迭代器。 - 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
- 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
适用场景:
- 访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
- 需要为一个聚合对象提供多种遍历方式。
- 为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。
以上是关于设计模式之迭代器模式的主要内容,如果未能解决你的问题,请参考以下文章