无涯

无所谓无 无所谓有

迭代器模式

迭代器模式

1. 引言

在软件开发中,我们经常需要遍历集合或容器对象。传统的遍历方式往往需要暴露集合内部的数据结构,这样会破坏封装性,同时也不够灵活。而迭代器模式能够提供一种统一的遍历接口,使得客户端代码可以独立于集合的具体实现方式进行遍历操作。

2. 痛点例子与解决方案

假设我们有一个存储学生信息的集合,我们希望能够按照先序遍历的方式遍历这个集合,并输出每个学生的姓名。传统的方式可能是通过索引进行遍历,但是这样会暴露集合内部的实现细节,而且不够灵活,不方便扩展。

1
2
3
4
5
6
7
8
复制代码List<String> students = new ArrayList<>();
students.add("Tom");
students.add("Jerry");
students.add("Alice");

for (int i = 0; i < students.size(); i++) {
System.out.println(students.get(i));
}

3. 迭代器模式详解

迭代器模式提供了一种统一的遍历接口,使得客户端代码可以独立于集合的具体实现方式进行遍历操作。它将遍历逻辑封装在迭代器对象中,客户端通过调用迭代器的方法来遍历集合。

迭代器模式包含以下几个角色:

  • 迭代器(Iterator):定义了遍历集合的接口,包含了获取下一个元素、判断是否还有元素等方法。
  • 具体迭代器(ConcreteIterator):实现迭代器接口,负责实现遍历集合的具体逻辑。
  • 集合(Collection):定义了集合对象的接口,包含了获取迭代器的方法。
  • 具体集合(ConcreteCollection):实现集合接口,负责创建具体迭代器对象。

使用迭代器模式重构上述例子,我们可以将遍历逻辑封装在迭代器中,客户端只需要通过调用迭代器的方法就可以按照先序遍历的方式遍历集合,而不需要关心集合的具体实现方式。

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
复制代码import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

interface PreorderIterator<T> extends Iterator<T> {
boolean hasNext();

T next();
}

interface PreorderCollection<T> extends Iterable<T> {
PreorderIterator<T> iterator();
}

class Student {
private String name;

public Student(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

class StudentCollection implements PreorderCollection<Student> {
private List<Student> students;

public StudentCollection() {
students = new ArrayList<>();
}

public void addStudent(Student student) {
students.add(student);
}

@Override
public PreorderIterator<Student> iterator() {
return new PreorderStudentIterator(students);
}
}

class PreorderStudentIterator implements PreorderIterator<Student> {
private List<Student> students;
private int position;

public PreorderStudentIterator(List<Student> students) {
this.students = students;
position = 0;
}

@Override
public boolean hasNext() {
return position < students.size();
}

@Override
public Student next() {
if (!hasNext()) {
throw new NoSuchElementException();
}

Student student = students.get(position++);
return student;
}
}

public class Main {
public static void main(String[] args) {
StudentCollection studentCollection = new StudentCollection();
studentCollection.addStudent(new Student("Tom"));
studentCollection.addStudent(new Student("Jerry"));
studentCollection.addStudent(new Student("Alice"));

PreorderIterator<Student> iterator = studentCollection.iterator();

while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student.getName());
}
}
}

4. 迭代器模式的优劣点

优点:

  • 提供了一种统一的遍历接口,客户端代码可以独立于集合的具体实现方式进行遍历操作。
  • 封装了集合内部的数据结构,提高了代码的灵活性和可维护性。
  • 支持多种遍历方式,可以同时存在多个迭代器。

缺点:

  • 增加了代码的复杂性,需要额外的迭代器对象。
  • 对于一些简单的集合,使用迭代器模式可能会过于繁琐。

5. 二叉树迭代器与foreach遍历

在现实中,二叉树是一种常见的数据结构。我们可以实现一个二叉树迭代器,使得客户端代码可以使用foreach语法糖来按照先序遍历的方式遍历二叉树。

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
64
65
66
67
68
69
70
71
72
复制代码class TreeNode {
int val;
TreeNode left;
TreeNode right;

public TreeNode(int val) {
this.val = val;
}
}

class BinaryTree implements PreorderCollection<Integer> {
private TreeNode root;

public BinaryTree(TreeNode root) {
this.root = root;
}

@Override
public PreorderIterator<Integer> iterator() {
return new PreorderTreeIterator(root);
}
}

class PreorderTreeIterator implements PreorderIterator<Integer> {
private Stack<TreeNode> stack;

public PreorderTreeIterator(TreeNode root) {
stack = new Stack<>();
if (root != null) {
stack.push(root);
}
}

@Override
public boolean hasNext() {
return !stack.isEmpty();
}

@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}

TreeNode node = stack.pop();
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
return node.val;
}
}

public class Main {
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
root.right.right = new TreeNode(7);

BinaryTree binaryTree = new BinaryTree(root);

for (int val : binaryTree) {
System.out.print(val + " ");
}
}
}

结束语

本文详细介绍了迭代器模式的原理和应用,并通过一个痛点例子引出了迭代器模式的解决方案。同时,还提供了现实中二叉树的迭代器示例,并展示了如何使用foreach语法糖来按照先序遍历的方式遍历二叉树。迭代器模式提供了一种灵活且封装的遍历方式,可以提高代码的可维护性和可扩展性。希望本文能够帮助您理解迭代器模式的原理和应用。

至此,本文介绍了迭代器模式的原理、解决方案以及优劣点,并提供了现实中二叉树的迭代器示例。