在软件开发中,有一些类可以存储多个成员对象(元素),这些类通常称为聚合类,对应的对象称为聚合对象。聚合对象拥有两个职责,一个是存储数据,一个是遍历数据,前者是聚合对象的基本职责,后者是可以变化以及分离的,因此,可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称之为“迭代器”的对象中,又迭代器来提供遍历聚合对象内部数据的行为。
迭代器模式:提供一种方法来访问对象,而不用暴露这个对象的内部表示,别名叫游标。
迭代器模式是一种对象行为型模式。
Iterator
(抽象迭代器):定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,比如first
(获取第一个元素),next
(获取下一个元素),hasNext
(判断是否有下一个元素),currentItem
(获取当前元素)ConcreteIterator
(具体迭代器):实现了抽象迭代器,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录聚合对象中所处的当前位置,通常游标是一个非负整数Aggregate
(抽象聚合类):用于存储和管理元素对象,声明一个createIterator
方法创建一个迭代器对象,充当抽象迭代器的工厂角色ConcreteAggregate
(具体聚合类):实现了抽象聚合类中的createIterator
方法,返回一个具体迭代器实例interface Iterator
{
String first();
String next();
boolean hasNext();
String currentItem();
}
interface Aggregate
{
Iterator createIterator();
void add(String s);
}
创建迭代器通过createIterator
,add
用于增添元素。
class ConcreteAggregate implements Aggregate
{
List<String> list = new ArrayList<>();
@Override
public Iterator createIterator()
{
return new ConcreteIterator(list);
}
@Override
public void add(String s)
{
list.add(s);
}
}
在创建迭代器方法中,通过构造方法把集合对象注入到具体迭代器中。
class ConcreteIterator implements Iterator
{
private int cursor;
private List<String> list;
public ConcreteIterator(List<String> list)
{
this.list = list;
this.cursor = -1;
}
@Override
public String first()
{
return list.size() > 0 ?
list.get(cursor = 0) :
null;
}
@Override
public String next()
{
return list.get(
cursor + 1 < list.size() ? ++cursor : cursor
);
}
@Override
public boolean hasNext()
{
return cursor+1 < list.size();
}
@Override
public String currentItem()
{
return list.get(cursor);
}
}
具体迭代器中包含了一个游标,用于记录当前访问的位置。构造方法中将游标初始化为-1而不是初始化为0,这样第一次使用next
时便会访问第一个元素。
public static void main(String[] args)
{
Aggregate aggregate = new ConcreteAggregate();
aggregate.add("111");
aggregate.add("222");
aggregate.add("jksdfjksdjkfk");
aggregate.add("m,xcvm,xcm,v");
Iterator iterator = aggregate.createIterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}
}
客户端针对抽象聚合类以及抽象迭代器编程,通过聚合对象创建迭代器后,首先使用haxNext
判断,接着使用next
获取其中元素。
设计一个系统对客户数据以及商品数据进行遍历,使用迭代器模式进行设计。
这个例子和上面的其实差不多,不过是反向迭代器方法,另外为了更贴近实际环境使用,抽象迭代器以及聚合类都使用了泛型设计:
Iterator
AbstarctList
ObjectList
ObjectIterator
Product
+Customer
首先设计抽象迭代器:
interface Iterator<T>
{
T next();
boolean hasNext();
String nextName() throws UnsupportedOperationException;
boolean hasNextName() throws UnsupportedOperationException;
void setProduct();
}
nextName()
以及hasNextName()
方法是对Customer
类型生效的,对于Product
会抛出异常。setProduct()
表示设置聚合元素的类型为Product
。
接着是抽象聚合类的设计:
interface AbstractList<T>
{
Iterator<T> iterator();
Iterator<T> reversedIterator();
void add(T s);
}
添加了一个反向迭代器实现。
然后是具体聚合类的设计:
class ObjectList<T> implements AbstractList<T>
{
List<T> list = new ArrayList<>();
@Override
public Iterator<T> iterator()
{
return new ObjectIterator<T>(list,false);
}
@Override
public void add(T s)
{
list.add(s);
}
@Override
public Iterator<T> reversedIterator()
{
return new ObjectIterator<T>(list,true);
}
}
内部还有一个List
存储聚合元素,iterator
返回正向迭代器,构造方法里面的布尔值表示是否为反向迭代器,reversedIterator
表示返回一个单向迭代器,与正向的唯一不同就是传入具体迭代器的构造方法中的布尔值。true
表示是反向迭代器,否则是正向。
最后是具体迭代器类:
class ObjectIterator<T> implements Iterator<T>
{
private int cursor;
private List<T> list;
private boolean reversed;
private boolean isProduct = false;
public ObjectIterator(List<T> list,boolean reversed)
{
this.list = list;
this.reversed = reversed;
this.cursor = (reversed ? list.size() : -1);
}
@Override
public void setProduct()
{
isProduct = true;
}
@Override
public T next()
{
return list.get(
reversed ?
( cursor - 1 >= 0 ? --cursor : cursor ) :
( cursor + 1 < list.size() ? ++cursor : cursor )
);
}
@Override
public boolean hasNext()
{
return reversed ?
cursor-1 >= 0 :
cursor+1 < list.size();
}
@Override
public String nextName() throws UnsupportedOperationException
{
if(isProduct)
throw new UnsupportedOperationException("商品迭代器不支持该操作");
return ((Customer)next()).getName();
}
@Override
public boolean hasNextName() throws UnsupportedOperationException
{
if(isProduct)
throw new UnsupportedOperationException("商品迭代器不支持该操作");
return hasNext();
}
}
构造方法中初始化聚合元素以及一个布尔值reversed
,表示是否为反向迭代器,游标根据reversed
设置为-1
或list.size()
。对于next
以及hasNext
方法,都需要判断是否为反向迭代器,返回对应的结果。对于nextName
以及hasNextName
,由于这两个方法仅对Customer
类生效,因此如果是Product
类直接抛出异常。
其他:
class Product
{
private String id;
private int num;
public Product(){}
public Product(String id,int num) {
this.id = id;
this.num = num;
}
public String getId() {
return this.id;
}
public int getNum() {
return this.num;
}
@Override
public String toString()
{
return "商品id:"+id+"\t商品数量:"+num;
}
}
class Customer
{
private String id;
private String name;
public Customer(String id,String name)
{
this.id = id;
this.name = name;
}
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
@Override
public String toString()
{
return "顾客id:"+id+"\t顾客名字:"+name;
}
}
测试类:
public static void main(String[] args)
{
Customer customer1 = new Customer("id1","name1");
Customer customer2 = new Customer("id2","name2");
Customer customer3 = new Customer("id3","name3");
AbstractList<Customer> customerList = new ObjectList<>();
customerList.add(customer1);
customerList.add(customer2);
customerList.add(customer3);
Iterator<Customer> customerIterator = customerList.iterator();
while(customerIterator.hasNext())
System.out.println(customerIterator.next());
customerIterator = customerList.reversedIterator();
while(customerIterator.hasNext())
System.out.println(customerIterator.next());
System.out.println();
customerIterator = customerList.iterator();
while(customerIterator.hasNextName())
System.out.println(customerIterator.nextName());
customerIterator = customerList.reversedIterator();
while(customerIterator.hasNextName())
System.out.println(customerIterator.nextName());
System.out.println();
Product product1 = new Product("product id 1",1);
Product product2 = new Product("product id 2",2);
Product product3 = new Product("product id 3",3);
AbstractList<Product> productList = new ObjectList<>();
productList.add(product1);
productList.add(product2);
productList.add(product3);
Iterator<Product> productIterator = productList.iterator();
while(productIterator.hasNext())
System.out.println(productIterator.next());
productIterator = productList.reversedIterator();
while(productIterator.hasNext())
System.out.println(productIterator.next());
System.out.println();
try
{
productIterator = productList.iterator();
productIterator.setProduct();
while(productIterator.hasNextName())
System.out.println(productIterator.nextName());
}
catch(Exception e)
{
e.printStackTrace();
}
}
首先创建了三个Customer
,接着添加到customerList
中,从customerList
中的iterator
获取正向迭代器以及从reversedIterator
获取正向迭代器,两种遍历方式的迭代器可以使用同样的语句实现遍历:
while(customerIterator.hasNext())
System.out.println(customerIterator.next());
对于Product
,由于hasNextName
以及nextName
声明了抛出异常,因此测试输出如下:
上面的例子可以看到在具体聚合类以及具体迭代器之间存在关联关系,具体迭代器需要维持一个对具体聚合对象(或里面的集合)的引用,除了使用关联关系外,还可以将迭代器设计为聚合类的内部类,比如JDK中的AbstractList
:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//...
private class Itr implements Iterator<E> {
//...
}
//...
}
使用类似的方式重新设计上面的例子:
class ObjectList<T> implements AbstractList<T>
{
List<T> list = new ArrayList<>();
@Override
public Iterator<T> iterator()
{
return new ObjectIterator(false);
}
@Override
public void add(T s)
{
list.add(s);
}
@Override
public Iterator<T> reversedIterator()
{
return new ObjectIterator(true);
}
private class ObjectIterator implements Iterator<T>
{
private int cursor;
private boolean reversed;
private boolean isProduct = false;
public ObjectIterator(boolean reversed)
{
this.reversed = reversed;
this.cursor = (reversed ? list.size() : -1);
}
//...
}
}
改变的就是具体迭代器的构造方法,不需要注入聚合对象了,另外也取消了泛型的声明。
Iterator
JDK(OpenJDK11.0.2)中的Collection
方法摘录如下:
public interface Collection<E> extends Iterable<E> {
//...
Iterator<E> iterator();
//...
}
该方法用于返回一个迭代器,以便遍历聚合类中的元素,其中Iterator
定于如下:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
//...
}
default void forEachRemaining(Consumer<? super E> action) {
//...
}
}
其中:
hasNext
:用于判断聚合对象是否存在下一个元素,需要在调用next
之前调用next
:将下标移至下一个元素,并返回游标所越过的那个元素的引用,也就是获取下一个元素remove
:删除上一次next
的返回的元素forEachRemaining
:用于对剩余元素进行的操作,比如一个集合有10个元素,使用迭代器遍历了前5个,则使用该方法会遍历剩下的元素,也就是后5个Java迭代器原理如图:
第一个next
被调用时,迭代器游标由0号位置移到1号位置,也就是移动到元素1以及元素2之间,接着返回游标越过的元素,也就是元素1。下一次调用next
时,游标继续移动,从1号位置移动到2号位置,并返回越过的元素,也就是元素2。对于remove
来说,删除上一次next
返回的元素,也就是如果此时调用remove
会删除元素2。
也就是在调用remove
之前至少需要调用一次next
,如果不调用next
的话,会抛出异常:
ListIterator
JDK中的List
接口除了继承Collection
接口的iterator
外,还增加一个listIterator
,专门用于创建ListIterator
类型的迭代器。用于遍历集合已经有了Iterator
,但是这个迭代器只能用于正向遍历,而ListIterator
的出现能解决逆向遍历的问题,因为其中提供了hasPrevious
以及previous
等方法。例子如下:
public static void main(String[] args) {
List<String> s = new ArrayList<>();
s.add("1111");
s.add("2222");
s.add("3333");
ListIterator<String> it = s.listIterator();
while(it.hasNext())
System.out.println(it.next());
System.out.println();
while(it.hasPrevious())
System.out.println(it.previous());
}
实现完整的逆向遍历时,需要先将游标移动到末尾,也就是不断调用next
直到末尾,才能实现调用previous
进行逆向遍历。
Iterator
就无法实现逆向遍历,设计一个考虑全面的抽象迭代器并不是一件容易的事如果觉得文章好看,欢迎点赞。
同时欢迎关注微信公众号:氷泠之路。