本文转自“雨夜随笔”公众号,欢迎关注
迭代器模式mp.weixin.qq.com在代码中,我们会用到各种各样的List或者Map。他们提供了一种方式来存储和查找数据,而在他们的背后都隐藏的迭代器模式的思想。
意图
迭代器模式是行为型模式的一种,设计的意图就是在不暴露数据表现形式(列表,栈或者树等)的情况下遍历集合中的所有元素。
问题
在代码中,我们无可避免的需要面对数据。而数据就需要相应的存储容器,而在各式各样的容器中,集合就是其中最为常用的数据类型之一。
集合既可以使用简单的列表来存储元素,也可以用栈、树、图或者其他数据结构来存储。
无论集合是采用什么数据结构来存储数据,都必须提供对其存储元素的访问方式。因为我们存储的数据最终还是要被使用的,不然就没有任何价值。所以集合需要提供一种遍历元素的方式,并且保证遍历的过程不会访问到重复的元素。
这个说起来也很简单,因为如果我们的集合基于列表,那么我们可以很方便的通过内存来进行读取。但是如果是基于复杂的数据结构呢?比如说树,就有前序遍历,后序遍历和中序遍历。并且随着需求的改变,我们可以需要通过别的方式进行遍历。
所以说,我们不能一有需求改动,就重新实现一遍遍历算法然后加入到集合对象的方法中。
同时我们如果没有一种通用的方式来解耦集合,那么我们的代码将不得不与特定集合类进行耦合。
解决方法
那么面对上面的问题,我们可以尝试将集合的遍历方式进行抽象,将遍历行为抽取为单独的迭代器对象。通过这种方式,我们只需要定义好迭代器的接口,这样当我们需要新的遍历方法,我们只需要定义一个新的迭代器对象,然后再客户端中引入就可以了。
那么我们现在的就是对遍历方式来进行抽象,来满足我们实际的业务需要。
// 方式1
public interface Iterator<E> {
boolean hasNext(); // 容器内是否有下一个元素
void next(); // 移动遍历位置
E currentItem(); // 当前指向的元素
}
// 方式2
public interface Iterator<E> {
boolean hasNext(); // 容器内是否有下一个元素
E next(); // 获取指向的元素并移动遍历位置
}
这两种方式的区别在于:
- 第一种方式的定义更加灵活,因为我们可以在不移动游标的情况下查询当前元素。
- 第二种方式更适合遍历场景,这种可以减少复杂度。
代码实现
import java.util.ArrayList;
public class ListIterator {
// 内部存储
private ArrayList<Integer> mValues = new ArrayList<>();
// 当前索引
private Integer mCurrentItem = 0;
// 这里只是简单的展示,实际上的要更复杂
// 要考虑遍历过程中增加元素的问题
// 我们将在实战中详细说明
public void addItem(int value){
mValues.add(value);
}
// 是否有下一个元素
public boolean hasNext(){
if(mCurrentItem + 1 <= mValues.size()){
return true;
}
return false;
}
// 索引后移
public void next(){
if(hasNext()){
mCurrentItem++;
} else {
throw new IndexOutOfBoundsException("no next value");
}
}
// 获取当前元素
public int currentItem(){
return mValues.get(mCurrentItem);
}
}
使用起来也很方便
public class Application {
public static void main(String[] args){
ListIterator iterator = new ListIterator();
iterator.addItem(1);
iterator.addItem(2);
iterator.addItem(3);
for(; iterator.hasNext(); iterator.next()){
System.out.printf("%d ", iterator.currentItem());
}
}
}
// 输出:1 2 3
总结
迭代器模式是行为型设计模式的一种,主要是为了关注集合类对象和使用者之间的交互。通过抽象集合类的遍历行为,来成功解耦使用者和集合类对象。
迭代器模式的设计思想就是抽象集合类对象的遍历行为,然后屏蔽集合类的内部存储结构和解决不同遍历行为的差异。通过迭代器模式我们可以在对集合进行不同形式的遍历时,最小化减少客户端和集合类的修改。