迭代器模式
迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。
迭代器模式结构
-
迭代器 (Iterator) 接口声明了遍历集合所需的操作: 获取下一个元素、 获取当前位置和重新开始迭代等。
-
具体迭代器 (Concrete Iterators) 实现遍历集合的一种特定算法。 迭代器对象必须跟踪自身遍历的进度。 这使得多个迭代器可以相互独立地遍历同一集合。
-
集合 (Collection) 接口声明一个或多个方法来获取与集合兼容的迭代器。 请注意, 返回方法的类型必须被声明为迭代器接口, 因此具体集合可以返回各种不同种类的迭代器。
-
具体集合 (Concrete Collections) 会在客户端请求迭代器时返回一个特定的具体迭代器类实体。 你可能会琢磨, 剩下的集合代码在什么地方呢? 不用担心, 它也会在同一个类中。 只是这些细节对于实际模式来说并不重要, 所以我们将其省略了而已。
-
客户端 (Client) 通过集合和迭代器的接口与两者进行交互。 这样一来客户端无需与具体类进行耦合, 允许同一客户端代码使用各种不同的集合和迭代器。
客户端通常不会自行创建迭代器, 而是会从集合中获取。 但在特定情况下, 客户端可以直接创建一个迭代器 (例如当客户端需要自定义特殊迭代器时)。
迭代器模式通用代码:
//抽象迭代器
interface Iterator{
//遍历下一个元素
public Object next();
//是否遍历到尾部
public boolean hasNext();
//删除当前指向元素
public boolean remove();
}
//具体迭代器
public class ConcreteIterator implements Iterator{
private Vector vector = new Vector();
private int cursor= 0;//当前游标
public ConcreteIterator(Vector _vector){
this.vector = _vector;
}
//是否遍历到尾部
public boolean hasNext(){
if(this.cursor==this.vector.size()){
return false;//false 代表到尾部
}else{
return true;
}
}
//遍历下一个元素
public Object next(){
Object result = null;
if(this.hasNext()){
result = this.vector.get(this.cursor++);
}else{
result = null;
}
return result;
}
//删除当前指向元素
public boolean remove() {
this.vector.remove(this.cursor);
return true;
}
}
//抽象容器
interface Aggregate {
Iterator createIterator();
}
// 具体聚合类
class ConcreteAggregate implements Aggregate {
private Vector vector = new Vector();
public void add(Object obj) {
vector.add(obj);
}
public Object get(int index) {
return vector.get(index);
}
public int size() {
return vector.size();
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(vector);
}
}
// 客户端示例
public class Client {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate.add("Item 1");
aggregate.add("Item 2");
aggregate.add("Item 3");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
迭代器模式适用场景
-
当集合背后为复杂的数据结构, 且你希望对客户端隐藏其复杂性时 (出于使用便利性或安全性的考虑), 可以使用迭代器模式。
迭代器封装了与复杂数据结构进行交互的细节, 为客户端提供多个访问集合元素的简单方法。 这种方式不仅对客户端来说非常方便, 而且能避免客户端在直接与集合交互时执行错误或有害的操作, 从而起到保护集合的作用。
-
使用该模式可以减少程序中重复的遍历代码。
重要迭代算法的代码往往体积非常庞大。 当这些代码被放置在程序业务逻辑中时, 它会让原始代码的职责模糊不清, 降低其可维护性。 因此, 将遍历代码移到特定的迭代器中可使程序代码更加精炼和简洁。
-
如果你希望代码能够遍历不同的甚至是无法预知的数据结构, 可以使用迭代器模式。
该模式为集合和迭代器提供了一些通用接口。 如果你在代码中使用了这些接口, 那么将其他实现了这些接口的集合和迭代器传递给它时, 它仍将可以正常运行。
识别方法: 迭代器可以通过导航方法 (例如
next
和previous
等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。
迭代器模式优缺点
优点:
-
单一职责原则。 通过将体积庞大的遍历算法代码抽取为独立的类, 你可对客户端代码和集合进行整理。
-
开闭原则。 你可实现新型的集合和迭代器并将其传递给现有代码, 无需修改现有代码。
-
你可以并行遍历同一集合, 因为每个迭代器对象都包含其自身的遍历状态。
-
相似的, 你可以暂停遍历并在需要时继续。
缺点:
-
如果你的程序只与简单的集合进行交互, 应用该模式可能会矫枉过正。
-
对于某些特殊集合, 使用迭代器可能比直接遍历的效率低。
练手题目
题目描述
小明是一位老师,在进行班级点名时,希望有一个学生名单系统,请你实现迭代器模式提供一个迭代器使得可以按顺序遍历学生列表。
输入描述
第一行是一个整数 N (1 <= N <= 100), 表示学生的数量。
接下来的 N 行,每行包含一个学生的信息,格式为 姓名 学号
输出描述
输出班级点名的结果,即按顺序遍历学生列表,输出学生的姓名和学号
题解
迭代器模式代码:
import java.util.*;
// 迭代器接口
interface StudentIterator {
boolean hasNext();
Student next();
}
// 具体迭代器类
class ConcreteStudentIterator implements StudentIterator {
private List<Student> studentList;
private int index = 0;
public ConcreteStudentIterator(List<Student> studentList) {
this.studentList = studentList;
}
@Override
public boolean hasNext() {
return index < studentList.size() && studentList.get(index) != null;
}
@Override
public Student next() {
return studentList.get(index++);
}
}
// 学生列表系统接口
interface StudentListSystem {
void addStudent(String name, String id);
String getStudentInfo();
StudentIterator iterator();
}
// 学生类
class Student implements StudentListSystem {
private String name;
private String id;
private List<Student> studentList;
public Student() {
this.studentList = new ArrayList<>();
}
public Student(String name, String id) {
this.name = name;
this.id = id;
}
@Override
public void addStudent(String name, String id) {
this.studentList.add(new Student(name, id));
}
@Override
public String getStudentInfo() {
return name + " " + id;
}
@Override
public StudentIterator iterator() {
return new ConcreteStudentIterator(this.studentList);
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StudentListSystem studentList = new Student();
int n = scanner.nextInt();
scanner.nextLine();
for (int i = 0; i < n; i++) {
String[] studentInfo = scanner.nextLine().split(" ");
studentList.addStudent(studentInfo[0], studentInfo[1]);
}
StudentIterator iterator = studentList.iterator();
while (iterator.hasNext()) {
Student student = (Student) iterator.next();
System.out.println(student.getStudentInfo());
}
scanner.close();
}
}
Java 内置的 List
接口的 iterator()
方法实现。
import java.util.*;
// 学生类
class Student {
String name;
String id;
Student(String name, String id) {
this.name = name;
this.id = id;
}
String getInfo() {
return name + " " + id;
}
}
// 学生列表系统
class StudentListSystem {
private List<Student> students = new ArrayList<>();
void addStudent(String name, String id) {
students.add(new Student(name, id));
}
Iterator<Student> iterator() {
return students.iterator();
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StudentListSystem studentList = new StudentListSystem();
int n = scanner.nextInt();
scanner.nextLine();
for (int i = 0; i < n; i++) {
String[] s = scanner.nextLine().split(" ");
studentList.addStudent(s[0], s[1]);
}
Iterator<Student> iterator = studentList.iterator();
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student.getInfo());
}
scanner.close();
}
}