迭代器 Iterator
迭代器模式是一种行为设计模式,让你能在不暴露集合底层表现形式(列表、 栈和树等)的情况下遍历集合中所有的元素。
为什么要使用?
迭代器模式的对象职责:
在不暴露对象内部细节的情况下,提供对容器对象中各个元素进行访问的方法。
👉 减少程序中重复的遍历代码。
🎈 使用迭代器模式是将遍历算法作为容器对象自身的一种“属性方法”来使用,能够有效地避免写很多重复的代码,不会暴露其内部结构。
👉 隐藏统一遍历集合的方法逻辑。
🎈 迭代器模式把对不同集合类的访问逻辑抽象出来,在不同暴露集合内部结构的情况下,隐藏不同集合遍历需要使用的算法,同时能够对外提供更为简便的访问算法接口。
模式结构
- 迭代器(Iterator)接口声明了遍历集合所需的操作:获取下一个元素、获取当前位置和重新开始迭代等。
- 具体迭代器(Concrete Iterators)实现遍历集合的一种特定算法。迭代器对象必须跟踪自身遍历的进度。这使得多个迭代器可以相互独立地遍历同一集合。
- 集合(Collection)接口声明一个或多个方法来获取与集合兼容的迭代器。请注意,返回方法的类型必须被声明为迭代器接口,因此具体集合可以返回各种不同种类的迭代器。
- 具体集合(Concrete Collections)会在客户端请求迭代器时返回一个特定的具体迭代器类实体。
- 客户端(Client)通过集合和迭代器的接口与两者进行交互。这样一来客户端无需与具体类进行耦合,允许同一客户端代码使用各种不同的集合和迭代器。客户端通常不会自行创建迭代器,而是会从集合中获取。但在特定情况下,客户端可以直接创建一个迭代器(例如当客户端需要自定义特殊迭代器时)。
迭代器模式的类图:
模式实现
该示例使用迭代器模式为 BookShelf
书架类提供了一个迭代器 BookShelfIterator
,实现了遍历书架的功能。
示例程序的类图
代码实现
迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package example;
public interface Iterator {
boolean hasNext();
Object next(); }
|
具体迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package example;
public class BookShelfIterator implements Iterator { private BookShelf bookShelf; private int index = 0;
public BookShelfIterator(BookShelf bookShelf) { this.bookShelf = bookShelf; }
@Override public boolean hasNext() { if (index >= bookShelf.size()) { return false; } return true; }
@Override public Object next() { Book book = bookShelf.get(index); index++; return book; } }
|
集合
1 2 3 4 5 6 7 8 9 10
| package example;
public interface Aggregate {
Iterator iterator(); }
|
具体集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| package example;
public class BookShelf implements Aggregate { private Book[] books; private int last = 0; private int size;
public BookShelf(int maxSize) { this.size = maxSize; this.books = new Book[maxSize]; }
public Book get(int index) { if (index >= 0 && index < size) { return books[index]; } return null; }
public void append(Book book) { if (last >= size) { this.size = 2 * this.size; Book[] expBooks = new Book[this.size]; for (int i = 0; i < this.books.length; i++) { expBooks[i] = this.books[i]; } this.books = expBooks; } books[last] = book; last++; }
public int size() { return last; }
@Override public Iterator iterator() { return new BookShelfIterator(this); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package example;
public class Book { private String name;
public Book(String name) { this.name = name; }
public String getName() { return name; } }
|
代码测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import example.Book; import example.BookShelf; import example.Iterator;
public class Test { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(2); bookShelf.append(new Book("设计模式")); bookShelf.append(new Book("数据结构")); bookShelf.append(new Book("软件工程")); bookShelf.append(new Book("软件建模")); Iterator it = bookShelf.iterator(); int stop = 0; while (it.hasNext()) { stop++; Book book = (Book) it.next(); System.out.println("1: " + book.getName()); if (stop == 2) { System.out.println("-- 中断遍历,但是迭代器仍保留遍历时中断的下标! --"); break; } } while (it.hasNext()) { Book book = (Book) it.next(); System.out.println("2: " + book.getName()); } } }
|
输出结果
1 2 3 4 5
| 1: 设计模式 1: 数据结构 -- 中断遍历,但是迭代器仍保留遍历时中断的下标! -- 2: 软件工程 2: 软件建模
|
从测试和结果中可以得出,迭代器模式能让我们暂停遍历并在需要时继续。
常用场景和解决方案
- 出于便利性或安全性等原因,希望对客户端隐藏其遍历算法复杂性时。
- 需要简化重复的循环遍历逻辑时,使用该模式可以减少程序中重复的遍历代码。
- 如果你希望代码能够遍历不同的甚至是无法预知的数据结构,可以使用迭代器模式。该模式为集合和迭代器提供了一些通用接口。如果你在代码中使用了这些接口,那么将其他实现了这些接口的集合和迭代器传递给它时,它仍将可以正常运行。
模式的优缺点
优点 |
缺点 |
单一职责原则。通过将体积庞大的遍历算法代码抽取为独立的类,你可对客户端代码和集合进行整理。 |
如果你的程序只与简单的集合进行交互,应用该模式可能会矫枉过正。 |
开闭原则。你可实现新型的集合和迭代器并将其传递给现有代码,无需修改现有代码。 |
对于某些特殊集合,使用迭代器可能比直接遍历的效率低。 |
你可以并行遍历同一集合,因为每个迭代器对象都包含其自身的遍历状态。 |
增加子类数量或系统复杂性。 |
相似的,你可以暂停遍历并在需要时继续。 |
|
拓展知识
- 你可以使用迭代器模式来遍历组合模式树。
- 你可以同时使用工厂方法模式和迭代器来让子类集合返回不同类型的迭代器,并使得迭代器与集合相匹配。
- 你可以同时使用备忘录模式和迭代器来获取当前迭代器的状态,并且在需要的时候进行回滚。
- 可以同时使用访问者模式和迭代器来遍历复杂数据结构,并对其中的元素执行所需操作,即使这些元素所属的类完全不同。
🔙 设计模式
📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言!
🔗参考文献:
🌐 设计模式 –refactoringguru
▶️ bilibili-趣学设计模式;黄靖锋. –拉勾教育
📖 图解设计模式 /(日)结城浩著;杨文轩译. –北京:人民邮电出版社,2017.1