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