0x1、定义
原始定义
迭代器提供一种对容器对象中各个元素进行访问的方法,而又不需要暴露该对象的内部细节。
定义很好理解,上构成该模式的四个角色:
- Iterator (抽象迭代器类) → 定义统一的迭代器方法hasNext()和next(),用于判断当前集合是否还有其他对象及按顺序读取集合中的当前对象;
- ConcreteIterator (具体迭代器) → 实现抽象迭代器声明的方法,处理具体集合对象中对对象位置的偏移及具体对象数据的传输;
- Container (抽象容器类) → 抽象及创建迭代器类关联的方法,同时可添加其他集合类需要的方法;
- ConcreteContainer (具体容器类) → 实现抽象容器类中声明的方法,创建对应具体的迭代器类;
其实就是两类角色:容器 和 迭代器,写个简单示例帮助理解~
0x2、写个简单例子
// 歌曲实体 public class Music { private String name; private String singer; private long createTime; public Music(String name, String singer, long createTime) { this.name = name; this.singer = singer; this.createTime = createTime; } public String getName() { return name; } public String getSinger() { return singer; } public long getCreateTime() { return createTime; } @Override public String toString() { return "【" + name + "】- " + singer + " - " + createTime; } } // 抽象迭代器 public interface Iterator { // 最基本的两个方法 Music next(); boolean hasNext(); // 按需添加 Music currentItem(); Music first(); } // 抽象容器 public interface Container { Iterator createIterator(); } // 具体迭代器 public class ConcreteIterator implements Iterator { private Music[] musics; private int pos = 0; // 待遍历容器通过依赖注入传递到具体迭代器类中 public ConcreteIterator(Music[] musics) { this.musics = musics; } @Override public Music next() { return musics[pos++]; } @Override public boolean hasNext() { return pos < musics.length; } @Override public Music currentItem() { return musics[pos]; } @Override public Music first() { return musics[0]; } } // 具体容器 public class ConcreteContainer implements Container { private Music[] musics; public ConcreteContainer(Music[] musics) { this.musics = musics; } @Override public Iterator createIterator() { return new ConcreteIterator(musics); } } // 测试用例 public class IteratorTest { public static void main(String[] args) { Music[] musics = new Music[5]; musics[0] = new Music("We Sing. We Dance. We Steal Things.", "Jason Mraz", 20080513); musics[1] = new Music("Viva La Vida Death And All His Friends", "Coldplay", 20080617); musics[2] = new Music("华丽的冒险 ", "陈绮贞", 20050923); musics[3] = new Music("范特西 Fantasy", "周杰伦", 20010914); musics[4] = new Music("後。青春期的詩 后青春期的诗", "五月天", 20081023); Container container = new ConcreteContainer(musics); Iterator iterator = container.createIterator(); while (iterator.hasNext()) { System.out.println(iterator.currentItem()); iterator.next(); } } }
代码运行结果如下:
网络异常,图片无法展示
|
代码非常简单:
- 具体迭代器实现next()、hasNext()方法;
- 待遍历容器对象通过依赖注入传递到迭代器中;
- 容器通过createIterator()方法创建迭代器;
你可能或说过度设计了,上面的遍历操作,自己通过 for循环 或 foreach循环 都可以实现。
的确如此,那为啥还要给容器设计对应的迭代器呢?三个原因:
- ① 复杂数据结构(如图、树),有各种复杂的遍历方式(树的前中后序遍历、图的深广度优先遍历等),如果让客户端来实现这些遍历算法,势必会增加开发成本,而且容易出错;
- ② 把遍历逻辑放容器类里无疑增加了容器类的复杂性,应对复杂性的方法就是 拆分,可把遍历操作拆分到迭代类中;
- ③ 每个迭代器独享游标信息,创建多个不同迭代器,同时对同一个容器遍历而不互相影响;
在举个例子,现在需要按照歌曲时间升序遍历,只需要实现一个迭代器类:
public class OrderTimeIterator implements Iterator { private final Music[] musics; private int pos; public OrderTimeIterator(Music[] musics) { this.musics = new Music[musics.length]; System.arraycopy(musics, 0, this.musics, 0, musics.length); sortByTimeAsc(this.musics, 0, this.musics.length - 1); this.pos = 0; } // 快速排序 private void sortByTimeAsc(Music[] arr, int low, int high) { if(low > high) return; int i = low; int j = high; Music temp; Music anchor = arr[low]; while (i < j) { while (arr[j].getCreateTime() >= anchor.getCreateTime() && i < j) { j--; } while (arr[i].getCreateTime() <= anchor.getCreateTime() && i < j) { i++; } if(i < j) { temp = arr[j]; arr[j] = arr[i]; arr[i] = temp; } } arr[low] = arr[i]; arr[i] = anchor; sortByTimeAsc(arr, low, j -1); sortByTimeAsc(arr, j + 1, high); } ... // 其他实现方法同ConcreteIterator }