Menu
CoddyTech

迭代器模式

本课属于 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 循环。当你需要提供多种遍历方法,或向客户端代码隐藏复杂的内部结构时,迭代器模式就非常重要。

challenge icon

挑战

简单

让我们使用迭代器模式来构建一个播放列表系统!你将创建一个音乐播放列表,用来存储歌曲,并提供一个自定义迭代器,按顺序遍历歌曲,而不暴露歌曲的内部存储方式。这是迭代器模式的绝佳应用场景,让用户通过简洁、统一的接口浏览你的集合。

你将把代码分到四个文件中:

  • 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!"
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器