目录
1.概述
迭代器模式 (Iterator Pattern) 是一种行为型设计模式,它提供一种顺序访问聚合对象(如列表、集合等)中的元素,而无需暴露聚合对象的内部表示 。迭代器模式将遍历逻辑封装在一个迭代器对象中,使得我们可以使用统一的方式遍历不同类型的聚合对象,同时也可以简化客户端代码。
2.结构
迭代器模式主要包含以下角色:
- 抽象聚合 (Aggregate) 角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
- 具体聚合 (ConcreteAggregate) 角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器 (Iterator) 角色 :定义访问和遍历聚合元素的接口,通常包含
hasNext()
、next()
等方法。 - 具体迭代器 (Concretelterator) 角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
3.案例实现
【例】定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现,其类图如下:
具体实现代码如下:
Student.java
java
public class Student {
private String name;
private String number;
public Student() {
}
public Student(String name, String number) {
this.name = name;
this.number = number;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", number='" + number + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
3.1.抽象迭代器
StudentIterator.java
java
//抽象迭代器角色接口
public interface StudentIterator {
//判断是否还有元素
boolean hasNext();
//获取下一个元素
Student next();
}
3.2.具体迭代器
StudentIteratorImpl.java
java
//具体迭代器角色类
public class StudentIteratorImpl implements StudentIterator{
private List<Student> list;
//用来记录遍历时的位置
private int position = 0;
public StudentIteratorImpl(List<Student> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return position < list.size();
}
@Override
public Student next() {
//从集合中或者去指定位置的元素
Student currentStudent = list.get(position);
position++;
return currentStudent;
}
}
3.3.抽象聚合
StudentAggregate.java
java
//抽象聚合(容器)角色接口
public interface StudentAggregate {
//添加学生
void addStudent(Student stu);
//删除学生
void removeStudent(Student stu);
//获取迭代器对象
StudentIterator getStudentIterator();
}
3.4.具体聚合
StudentAggregateImpl.java
java
public class StudentAggregateImpl implements StudentAggregate{
private List<Student> list = new ArrayList<>();
@Override
public void addStudent(Student stu) {
list.add(stu);
}
@Override
public void removeStudent(Student stu) {
list.remove(stu);
}
//获取迭代器对象
@Override
public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
}
}
3.5.测试
Client.java
java
public class Client {
public static void main(String[] args) {
//创建聚合(容器)对象
StudentAggregateImpl aggregate = new StudentAggregateImpl();
Student student1 = new Student("Tom", "1001");
Student student2 = new Student("Mike", "1002");
Student student3 = new Student("Jerry", "1003");
Student student4 = new Student("Mary", "1004");
//添加元素
aggregate.addStudent(student1);
aggregate.addStudent(student2);
aggregate.addStudent(student3);
aggregate.addStudent(student4);
//删除元素
aggregate.removeStudent(student3);
//遍历聚合对象
// 1.获取迭代器对象
StudentIterator iterator = aggregate.getStudentIterator();
// 2.遍历
while (iterator.hasNext()){
// 3.获取元素
Student student = iterator.next();
System.out.println(student.toString());
}
}
}
结果如下:
java
Student{name='Tom', number='1001'}
Student{name='Mike', number='1002'}
Student{name='Mary', number='1004'}
4.优缺点
(1)迭代器模式的优缺点如下:
- 优点
- 简化客户端代码:迭代器模式将遍历逻辑封装在迭代器对象中,客户端代码只需通过迭代器接口与迭代器对象交互,无需关心具体的遍历算法和聚合对象的内部结构,从而简化了客户端代码。
- 统一遍历接口:迭代器模式提供一种统一的遍历接口,使得对不同类型的聚合对象可以使用相同的遍历方式,提供了更加灵活和通用的遍历操作。
- 支持多种遍历方式:通过不同的迭代器实现,迭代器模式支持多种遍历方式,如顺序遍历、逆序遍历等,使得遍历操作更加灵活可变。
- 解耦聚合对象与遍历操作:迭代器模式将遍历操作从聚合对象中分离出来,使得聚合对象的内部结构不再暴露给客户端,遵循了单一职责原则,提高了代码的可维护性和可扩展性。
- 缺点
- 对于某些较为复杂的聚合对象,实现迭代器可能会比较复杂,需要实现多个方法,并考虑并发访问的线程安全性,增加了代码的复杂性。
- 当遍历的聚合对象发生变化时,需要相应地修改迭代器的实现,可能会影响到迭代器的逻辑。
(2)总体来说,迭代器模式适用于需要统一遍历接口、封装遍历逻辑、解耦聚合对象与遍历操作的场景。它可以提供更加灵活、通用和可维护的遍历操作,但在某些情况下,可能会增加代码的复杂性。因此,在使用迭代器模式时需要综合考虑具体的设计需求和项目情况。
5.使用场景
(1)迭代器模式通常适用于以下场景:
- 需要遍历聚合对象的元素:当需要遍历并访问聚合对象中的元素,并且不希望暴露聚合对象的内部结构给客户端时,可以使用迭代器模式。它可以将遍历操作封装在迭代器中,提供一个统一的遍历接口。
- 需要对不同类型的聚合对象使用统一的遍历方式:迭代器模式提供了一种统一的遍历接口,使得对不同类型的聚合对象可以使用相同的遍历方式。这样可以增加代码的灵活性和可维护性,使得客户端代码更加通用。
- 需要支持多种遍历方式:如果需要支持多种遍历方式,如顺序遍历、逆序遍历等,可以通过不同的迭代器实现来实现不同的遍历方式。这样可以使遍历操作更加灵活可变。
- 需要屏蔽聚合对象的内部实现细节:当聚合对象的内部结构较为复杂,或者不希望暴露内部结构给客户端时,可以使用迭代器模式进行封装。迭代器模式将遍历逻辑封装在迭代器对象中,客户端无需关心聚合对象的具体实现细节。
- 需要遍历操作与聚合对象解耦:迭代器模式将遍历操作从聚合对象中分离出来,使得聚合对象的内部结构不再暴露给客户端。这样可以提高代码的可维护性和可扩展性,使遍历操作与聚合对象解耦。
(2)迭代器模式在现实生活中的应用也非常广泛,例如集合类、数据库查询结果等都可以使用迭代器模式来统一遍历操作接口。
6.JDK 源码解析------Iterator
(1)迭代器模式在 Java 的很多集合类中被广泛应用,接下来看下面的例子:
java
public class TestArratList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//添加元素
list.add("1001");
list.add("1002");
list.add("1003");
//删除元素
list.remove("1003");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
结果如下:
java
1001
1002
(2)看完这段代码是不是觉得很熟悉,它与我们上面的代码基本类似。单列集合都使用到了迭代器,所以现在以 ArrayList
举例来说明:
List
:抽象聚合类;ArrayList
:具体的聚合类;Iterator
:抽象迭代器;list.iterator()
:返回的是实现了 Iterator 接口的具体迭代器对象;
(3)具体来看看 ArrayList 的部分关键代码实现:
java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回元素的索引
int lastRet = -1; // 上一个返回元素的索引
int expectedModCount = modCount;
Itr() {}
//判断是否还有元素
public boolean hasNext() {
return cursor != size;
}
//获取下一个元素
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
//...
}
(4)这部分代码大致就是在 iterator 方法中返回了一个实例化的 Iterator
对象。Itr
是一个内部类,它实现了 Iterator 接口并重写了其中的抽象方法。此外,通过下图可知 ArrayList 与 Iterable 的关系:
java
package java.lang;
import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
(5)当我们在使用 Java 开发的时想使用迭代器模式的话,只要让我们自己定义的容器类实现 java.util.Iterable
并实现其中的 iterator()
方法使其返回一个 java.util.Iterator
的实现类就可以了。