一、概述
迭代器模式提供了一种方式去访问一个容器元素中的各个对象,而又不暴露该对象的内部细节,它是一种行为型的设计模式。迭代器就是为对集合类(聚合类)进行循环遍历,但是集合类已经存储了元素还要进行遍历的话,这就不符合单一职责原则。其实说白了,迭代器就是帮助集合类进行遍历的。
迭代器需要有4个核心结构:
- 抽象的迭代器角色:负责定义访问和遍历元素的接口;
- 具体的迭代器角色:实现迭代器接口,并要记录遍历中的当前位置;
- 抽象的容器角色:负责提供创建具体迭代器角色的接口;
- 具体的容器角色:实现创建具体迭代器角色的接口,这个具体的迭代器角色与该容器的结构相关;
迭代器在Java的集合框架中的典型应用,通过Iterator迭代器来遍历集合,ListIterator是专门为遍历List而存在的。所以正是因为有了Java帮我们封装了的迭代器,我们在使用它时就比较简单,它也不太受到关注,平时就更少自己实现迭代器模式了。下面我们实现一个简单的Iterator迭代器和List容器。
二、使用
首先我们创建抽象的迭代器角色,它只负责两件事:访问元素和遍历元素,所以它只需要2个方法。
/**
* 自定义的抽象迭代器接口
*/
public interface MyIterator {
//返回下一个对象
Object next();
//是否还有下一个元素
boolean hasNext();
}
然后我们创建抽象的容器角色,它就是所有容器的高度抽象的接口,它依赖于我们上面创建的迭代器接口,我们这里只添加3个方法:添加元素、获取容器长度、获取迭代器。
/**
* 抽象的容器接口
*/
public interface MyCollection {
//添加元素
void add(Object o);
//容器大小
int size();
//迭代方法
MyIterator iterator();
}
再者我们创建具体的迭代器角色,其实就是需要在具体的逻辑中实现接口中的方法,并且要记录当前遍历的位置。hasNext方法判断很简单,只需要判断当前记录的位置是否大于了容器的长度;next方法需要获取容器中的元素,所以迭代器需要持有一个容器角色的引用。
/**
* 具体的迭代器
*/
public class MyArrayListIterator implements MyIterator{
//记录当前位置
private int currentIndex = 0;
private MyArrayList myArrayList;
public MyArrayListIterator(MyArrayList myArrayList){
this.myArrayList = myArrayList;
}
@Override
public Object next() {
Object o = null;
if(hasNext()){
o = myArrayList.get(currentIndex);
}
currentIndex++;
return o;
}
@Override
public boolean hasNext() {
return currentIndex < myArrayList.size();
}
}
最好就是创建具体的容器角色,代码中我们只是简单模拟了Java中的ArrayList类,有添加、获取、扩容等操作,都很简单就不再过多说明。需要注意的是iterator()方法,我们直接返回了MyArrayListIterator。
/**
* 具体的容器类
*/
public class MyArrayList implements MyCollection{
//List成员
private Object[] elementData;
//List长度
private int size;
public MyArrayList(int size) {
super();
this.elementData = new Object[size];
this.size = 0;
}
public MyArrayList(){
this(10);
}
@Override
public void add(Object o) {
if(this.size >= 10){
int newSize = size * 3;
elementData = Arrays.copyOf(elementData, newSize);
}
elementData[size] = o;
size++;
}
public Object get(int size){
return elementData[size];
}
@Override
public int size() {
return size;
}
@Override
public MyIterator iterator() {
return new MyArrayListIterator(this);
}
}
下面来测试一下。打印输出结果如下图。
MyArrayList arrayList = new MyArrayList();
arrayList.add("aaa");
arrayList.add("bbb");
arrayList.add("ccc");
arrayList.add("ddd");
MyArrayListIterator arrayListIterator = new MyArrayListIterator(arrayList);
while (arrayListIterator.hasNext()){
Log.e(TAG, "数据有:" + arrayListIterator.next());
}
代码也有需要改进的地方,比如MyArrayList可以改成泛型类,而不是直接用一个Object代替,这里只是为了说明迭代器的相关使用,可以自己下来改一下。
三、总结
迭代器模式使用场景不多,除了能在java集合框架中遍历的同时,能对元素进行修改(添加、删除等)的这个地方使用最多。从上面的例子中我们也可以看到迭代器也有一些优点:
- 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心;
- 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序、倒序遍历两种迭代器;
- 客户端可以通过相同的方式遍历不同的容器对象;
- 由于引入了抽象迭代器,所以我们进行扩展的时候会比较方便,无需修改源码,符合开闭原则;但这也可能会造成前期设计考虑不够完善,后面修改源码时所有的具体迭代器都会受到影响。
使用迭代器可能就没有直接用for循环遍历方便,在使用上稍微繁琐一些,这个就根据自己的情况来了。
github地址:github.com/leewell5717…