在开发中,我们经常使用到Iterator这个接口,我们很疑惑于这个接口的作用,认为集合已经实现了数据访问的方法,增加Iterator的意义在哪。本文我们将学习迭代器模式,用以探讨Iterator的作用。
提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示。
聚合对象拥有两个职责:1、存储数据;2、遍历数据。从依赖性来看,前者是聚合对象的基本职责,后者既是可以变化的,又是可分离的。将遍历数据的行为从聚合对象中分离出来,封装在一个被称为迭代器的对象中。由迭代器来提供遍历聚合对象内部数据的行为。
图 迭代器模式结构图
Iterator:抽象迭代器,定义了访问和遍历数据元素的接口。
ConcreteIterator:具体迭代器,实现了抽象迭代器接口,完成对聚合对象的遍历。同时通过游标来记录在聚合对象中所处的当前位置。
Aggregate:抽象聚合类,用于存储和管理元素对象。声明一个creteIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
ConcreteAggregate:具体聚合类。实现createIterator()方法,返回一个与该具体聚合类对应的具体迭代器实例。
public interface Iterator {
T first();
T next();
boolean hasNext();
T currentItem();
}
public class ConcreteIterator implements Iterator{
private final List list;
private int cursor = 0;
public ConcreteIterator(ConcreteAggregate list) {
this.list = list.getList();
}
@Override
public T first() {
return list.get(0);
}
@Override
public T next() {
T t = list.get(cursor);
cursor++;
return t;
}
@Override
public boolean hasNext() {
return cursor < list.size();
}
@Override
public T currentItem() {
return list.get(cursor);
}
}
public abstract class Aggregate {
protected final List list = new ArrayList<>();
public abstract Iterator createIterator();
public List getList() {
return list;
}
public void addItem(T item) {
list.add(item);
}
}
public class ConcreteAggregate extends Aggregate{
@Override
public Iterator createIterator() {
return new ConcreteIterator<>(this);
}
}
public class Client {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate<>();
aggregate.addItem("你好");
aggregate.addItem("JAVA");
aggregate.addItem("Hello");
aggregate.addItem("world");
aggregate.addItem("是谁说的");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 运行结果:
// 你好
// JAVA
// Hello
// world
// 是谁说的
}
}
具体迭代器类和具体聚合类之间存在双重关系,我们可以使用内部类(JDK中的迭代器类就是通过这种方法来实现的)来实现迭代器,这样可以对外界隐藏具体迭代器的细节。
public class InnerAggregate extends Aggregate {
@Override
public Iterator createIterator() {
return new InnerIterator();
}
private class InnerIterator implements Iterator{
private int cursor = 0;
@Override
public T first() {
return list.get(0);
}
@Override
public T next() {
T t = list.get(cursor);
cursor++;
return t;
}
@Override
public boolean hasNext() {
return cursor < list.size();
}
@Override
public T currentItem() {
return list.get(cursor);
}
}
}
public class Client {
public static void main(String[] args) {
Aggregate aggregate = new InnerAggregate<>();
aggregate.addItem("躺平");
aggregate.addItem("努力");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext())
System.out.println(iterator.next());
// 运行结果:
// 躺平
// 努力
}
}
Java提供了内置迭代器,像常用的List聚合接口,其继承了Collection接口。
图 ArrayList继承类图
图 Iterable与Collection 接口声明方法
ArrayList类使用了内部类Itr来实现内部迭代器。
图 ArrayList 的内部迭代器
图 ArrayList内部迭代器的继承类图
Java SE5引入了Iterable接口,该接口被foreach用来在序列中移动。(Collection继承了Iterable接口)
原理:foreach语句最终被编程器转换成对iterator.next()和iterator.hasNext()方法的调用。JDK屏蔽了这些实现细节。
图 foreach Java源代码与编译后的class对比图
优点:
缺点:
1)增加了类的个数,设计难度较大,需充分考虑到系统将来的扩展。