迭代器模式
本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 76 课,共 87 课。
迭代器模式是一种行为型设计模式,它提供了一种按顺序访问集合元素的方式,而不会暴露集合的底层结构。无论你操作的是数组、链表还是树,迭代器都能让你用统一的方式遍历元素。
这种模式将遍历逻辑与集合本身分离。它包含两个主要组件:用于定义遍历元素的方法的 Iterator 接口,以及为其集合创建迭代器的 Iterable(或 Aggregate):
interface Iterator<T> {
boolean hasNext();
T next();
}
interface Container<T> {
Iterator<T> createIterator();
}
class BookShelf implements Container<String> {
private String[] books;
private int count = 0;
public BookShelf(int size) {
books = new String[size];
}
public void addBook(String book) {
books[count++] = book;
}
public Iterator<String> createIterator() {
return new BookIterator();
}
private class BookIterator implements Iterator<String> {
private int index = 0;
public boolean hasNext() {
return index < count;
}
public String next() {
return books[index++];
}
}
}迭代器维护自己的遍历状态,因此多个迭代器可以独立遍历同一个集合。客户端无需了解集合如何存储数据,就能使用迭代器:
BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");
Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}Java 内置的 Iterable 和 Iterator 接口遵循这一确切模式,因此你可以对任何实现了 Iterable 的类使用增强型 for 循环。当你需要提供多种遍历方法,或向客户端代码隐藏复杂的内部结构时,迭代器模式就非常重要。
挑战
简单让我们使用迭代器模式来构建一个播放列表系统!你将创建一个音乐播放列表,用来存储歌曲,并提供一个自定义迭代器,按顺序遍历歌曲,而不暴露歌曲的内部存储方式。这是迭代器模式的绝佳应用场景,让用户通过简洁、统一的接口浏览你的集合。
你将把代码分到四个文件中:
Iterator.java:定义泛型Iterator<T>接口,其中包含两个方法:hasNext()返回一个布尔值,表示是否还有元素;next()返回下一个 T 类型的元素。Playlist.java:创建一个用于存放歌曲的聚合类。你的Playlist应该使用固定容量的 String 数组存储歌曲(容量通过构造函数传入),并跟踪已添加的歌曲数量。添加一个addSong(String song)方法,用于向播放列表中添加歌曲。你的 Playlist 需要一个
createIterator()方法,返回Iterator<String>。你可以通过创建一个名为PlaylistIterator的私有内部类来实现,该类实现你的 Iterator 接口。这个内部迭代器维护自己的索引位置,并遍历歌曲数组,依次返回每首歌曲。Song.java:创建一个简单的Song类,用于封装歌曲标题。它应该包含一个接收标题(String)的构造函数、一个getTitle()方法,以及一个toString()方法,返回Playing: [title]。Main.java:把迭代器系统组合起来!你会收到一个输入:由逗号分隔的歌曲标题列表(例如:Bohemian Rhapsody,Stairway to Heaven,Hotel California)。创建一个容量为 10 首歌曲的 Playlist。解析输入,并将每个歌曲标题添加到播放列表中。然后从播放列表获取一个迭代器,并使用它遍历所有歌曲,将每首歌曲封装在 Song 对象中并打印出来(显示为
Playing: [title])。遍历完所有歌曲后,在新的一行打印
Playlist complete!。
你会收到一个输入:由逗号分隔的歌曲标题字符串。
例如,输入 Yesterday,Imagine,Let It Be 时,输出为:
Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!注意,Main 类使用迭代器的 hasNext() 和 next() 方法遍历播放列表,而不需要了解底层数组结构。迭代器封装了所有遍历逻辑,使集合的内部表示对客户端代码保持隐藏!
亲手试试
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO: 创建一个可容纳 10 首歌曲的 Playlist
// TODO: 解析以逗号分隔的输入,并将每首歌的标题添加到播放列表中
// TODO: 使用 createIterator() 从播放列表获取迭代器
// TODO: 使用迭代器遍历所有歌曲
// 将每首歌封装成一个 Song 对象并打印出来
// TODO: 遍历完所有歌曲后,打印 "Playlist complete!"
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Java 在线编译器