1. 迭代器模式概述
概述
- 提供一种方法依次访问一个容器对象中各个元素,而又不需暴露该对象的内部细节
特点
- 迭代器的思想是依次访问、可读不可写,适合列表对外访问、隐藏内部细节、保证安全性
- 基本上每种语言都会在语言层面为所有列表提供迭代器,实际开发中拿来主义即可
Demo
需求:一个类中存在一个列表,这个列表需要提供给外部类访问,但不希望外部类修改其中的数据
分析:可以通过提供两个方法实现可读不可写
- 提供一个
String next()
方法,使得外部类可以按照次序,一条一条读取数据 - 提供一个
boolean hasNext()
方法,告知外部类是否还有下一条数据
- 提供一个
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13public class MyList {
private List<String> data = Arrays.asList("a", "b", "c");
private int index = 0;
public String next() {
// 返回数据后,将 index 加 1,使得下次访问时返回下一条数据
return data.get(index++);
}
public boolean hasNext() {
return index < data.size();
}
}客户端:使用一个 while 循环访问此列表
1
2
3
4
5
6
7
8
9
10public class Client {
@Test
public void test() {
MyList list = new MyList();
// 输出:abc
while (list.hasNext()) {
System.out.print(list.next());
}
}
}- 因为没有给外部类暴露 data 成员变量,所以可以保证数据是安全的
- 问题是:当遍历完成后,
hasNext()
方法会一直返回 false,无法再一次遍历了,所以必须在一个合适的地方把 index 重置为 0
迭代器:实际上,使用
next()
和hasNext()
来遍历列表是一个完全通用的方法,可以为其创建一个接口,取名 Iterator,即迭代器1
2
3
4
5
6public interface Iterator {
boolean hasNext();
String next();
}修改列表类:每次遍历时生成一个迭代器,将 index 变量放到迭代器中。因为每个迭代器都是新生成的,所以每次遍历时的 index 自然也就被重置成 0 了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class MyList {
private List<String> data = Arrays.asList("a", "b", "c");
// 每次生成一个新的迭代器,用于遍历列表
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator {
private int index = 0;
@Override
public boolean hasNext() {
return index < data.size();
}
@Override
public String next() {
return data.get(index++);
}
}
}客户端访问此列表:迭代器的核心就在于定义出
next()
方法和hasNext()
方法,让外部类使用这两个方法来遍历列表,以达到隐藏列表内部细节的目的1
2
3
4
5
6
7
8
9
10
11
12public class Client {
@Test
public void test() {
MyList list = new MyList();
// 获取迭代器,用于遍历列表
Iterator iterator = list.iterator();
// 输出:abc
while (iterator.hasNext()) {
System.out.print(iterator.next());
}
}
}Java 内置的 Iterator 接口:源码中使用了泛型使此接口更加通用
1
2
3
4public interface Iterator<E> {
boolean hasNext();
E next();
}ArrayList 源码:使用迭代器模式的部分代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class ArrayList<E> {
...
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
int cursor;
public boolean hasNext() {
return cursor < limit;
}
public E next() {
...
}
}
}Java 内置的 Iterable 接口:平时常用的 for-each 循环,也是迭代器模式的一种应用。在 Java 中,只要实现了 Iterable 接口的类,都被视为可迭代访问的
1
2
3
4public interface Iterable<T> {
Iterator<T> iterator();
...
}修改列表类继承 Iterable 接口:继承 Iterable 接口之后,就可以使用 for-each 来迭代访问列表中的数据了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class MyList implements Iterable<String> {
private List<String> data = Arrays.asList("a", "b", "c");
public Iterator<String> iterator() {
// 每次生成一个新的迭代器,用于遍历列表
return new Itr();
}
private class Itr implements Iterator<String> {
private int index = 0;
public boolean hasNext() {
return index < data.size();
}
public String next() {
return data.get(index++);
}
}
}客户端:使用 for-each 访问
1
2
3
4
5
6
7
8
9
10public class Client {
@Test
public void test() {
MyList list = new MyList();
// 输出:abc
for (String item : list) {
System.out.print(item);
}
}
}