迭代器模式(Iterator Pattern)
迭代器模式(Iterator Pattern)
迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。通过迭代器模式,客户端可以遍历整个集合,而不需要了解底层数据结构的具体实现细节。这不仅提高了代码的抽象层次,也增强了系统的灵活性和可扩展性。
迭代器模式概述
迭代器模式结构图
迭代器模式涉及的角色
- 迭代器接口(Iterator):声明了用于遍历集合的方法,如 hasNext() 和 next() 等。具体迭代器必须实现这些方法,以提供特定类型的遍历逻辑。
public interface Iterator<T> {
boolean hasNext();
T next();
}
- 具体迭代器(ConcreteIterator):实现了 Iterator 接口的具体类。
public class ConcreteIterator<T> implements Iterator<T> {
private final List<T> items;
private int position = 0;
public ConcreteIterator(List<T> items) {
this.items = items;
}
@Override
public boolean hasNext() {
return position < items.size();
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return items.get(position++);
}
}
- 聚合接口(Aggregate):接口或抽象类,声明了一个创建迭代器对象的方法。具体聚合类必须实现这个方法,以返回一个与之对应的迭代器实例。
import java.util.Iterator;
public interface Aggregate<T> {
Iterator<T> createIterator();
}
- 具体聚合(ConcreteAggregate):实现了 Aggregate 接口的具体类。
import java.util.ArrayList;
import java.util.List;
public class ConcreteAggregate<T> implements Aggregate<T> {
private final List<T> items = new ArrayList<>();
@Override
public Iterator<T> createIterator() {
return new ConcreteIterator<>(items);
}
// Methods to manipulate the collection, such as add and remove.
public void addItem(T item) {
items.add(item);
}
public void removeItem(T item) {
items.remove(item);
}
}
talk is cheap, show you my code
迭代器模式是一种常见的设计模式,Java的集合里面很多容器类就实现了迭代器模式。我们展示一下ArrayList中关于迭代器的部分源码
- 字段
- int cursor;:指向下一个要返回的元素的索引。
- int lastRet = -1;:记录上一次调用next()方法返回的元素索引,用于remove()操作;如果值为-1,则表示还没有调用过next()或者最近的一次next()之后已经调用了remove()。
- int expectedModCount;:在创建迭代器时保存的ArrayList的修改次数(modCount)。这个值用来检测在迭代过程中是否有其他线程或代码修改了ArrayList。
- 构造函数
Itr() {
this.expectedModCount = ArrayList.this.modCount;
}
构造函数初始化expectedModCount为当前ArrayList的modCount,以确保在迭代期间可以检测到结构化修改。
- hasNext() 方法
public boolean hasNext() {
return this.cursor != ArrayList.this.size;
}
检查是否还有更多元素可以迭代。当cursor小于ArrayList的大小时返回true,否则返回false。
- next() 方法
public E next() {
this.checkForComodification();
int i = this.cursor;
if (i >= ArrayList.this.size) {
throw new NoSuchElementException();
} else {
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
} else {
this.cursor = i + 1;
return elementData[this.lastRet = i];
}
}
}
- 首先调用checkForComodification()来验证自迭代器创建以来列表没有被修改。
- 检查cursor是否超出了列表大小,如果是则抛出NoSuchElementException。
- 确保cursor不超过内部数组长度,以防止并发修改异常。
- 更新cursor和lastRet,并返回当前元素。
- remove() 方法
public void remove() {
if (this.lastRet < 0) {
throw new IllegalStateException();
} else {
this.checkForComodification();
try {
ArrayList.this.remove(this.lastRet);
this.cursor = this.lastRet;
this.lastRet = -1;
this.expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException var2) {
throw new ConcurrentModificationException();
}
}
}
- 检查lastRet是否有效,即之前是否调用了next()方法且未调用过remove()。
- 再次调用checkForComodification()确保没有发生并发修改。
- 尝试从ArrayList中移除由lastRet指示的元素,并更新cursor、lastRet和expectedModCount。
- forEachRemaining(Consumer<? super E> action)
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
int size = ArrayList.this.size;
int i = this.cursor;
if (i < size) {
Object[] es = ArrayList.this.elementData;
if (i >= es.length) {
throw new ConcurrentModificationException();
}
while(i < size && ArrayList.this.modCount == this.expectedModCount) {
action.accept(ArrayList.elementAt(es, i));
++i;
}
this.cursor = i;
this.lastRet = i - 1;
this.checkForComodification();
}
}
这是一个优化过的批量处理剩余元素的方法,使用给定的动作(action)对每个剩余元素执行操作,直到所有元素都被处理完毕或发生并发修改。
- checkForComodification 方法
final void checkForComodification() {
if (ArrayList.this.modCount != this.expectedModCount) {
throw new ConcurrentModificationException();
}
}
此方法用于检测ArrayList是否在迭代过程中被修改。如果ArrayList的modCount与创建迭代器时保存的expectedModCount不匹配,则抛出ConcurrentModificationException,表明发生了非法的并发修改。
private class Itr implements Iterator<E> {
int cursor;
int lastRet = -1;
int expectedModCount;
Itr() {
this.expectedModCount = ArrayList.this.modCount;
}
public boolean hasNext() {
return this.cursor != ArrayList.this.size;
}
public E next() {
this.checkForComodification();
int i = this.cursor;
if (i >= ArrayList.this.size) {
throw new NoSuchElementException();
} else {
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
} else {
this.cursor = i + 1;
return elementData[this.lastRet = i];
}
}
}
public void remove() {
if (this.lastRet < 0) {
throw new IllegalStateException();
} else {
this.checkForComodification();
try {
ArrayList.this.remove(this.lastRet);
this.cursor = this.lastRet;
this.lastRet = -1;
this.expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException var2) {
throw new ConcurrentModificationException();
}
}
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
int size = ArrayList.this.size;
int i = this.cursor;
if (i < size) {
Object[] es = ArrayList.this.elementData;
if (i >= es.length) {
throw new ConcurrentModificationException();
}
while(i < size && ArrayList.this.modCount == this.expectedModCount) {
action.accept(ArrayList.elementAt(es, i));
++i;
}
this.cursor = i;
this.lastRet = i - 1;
this.checkForComodification();
}
}
final void checkForComodification() {
if (ArrayList.this.modCount != this.expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
Itr类实现了Iterator接口,提供了安全地遍历ArrayList的方法,并通过expectedModCount机制来保护迭代过程免受并发修改的影响。同时,它还支持remove()操作,允许在迭代过程中移除元素。这种设计保证了迭代的安全性和一致性,同时也符合Java集合框架的标准行为。
总结
迭代器模式的优点包括:
- 支持对集合对象的多种遍历:同一个聚合可以有多个遍历方式,且遍历方式可以独立于聚合对象存在。
- 简化了聚合类:将遍历逻辑从聚合类中分离出来,使得聚合类的代码更加简洁。
- 提高了代码的可维护性和可扩展性:增加新的聚合类和迭代器类都很方便,无需修改原有代码。
迭代器模式缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
- 设计难度大,要考虑系统将来的扩展。JDK内置的迭代器不支持逆向遍历,导致只能再增加一个新的子类ListIterator实现。
迭代器模式适用于以下场景:
- 当需要对一个聚合对象进行多种方式遍历时。
- 当需要对聚合对象提供多种遍历方式,且希望这些遍历方式可以独立于聚合对象存在时。
- 当需要封装一个复杂的数据结构,并希望对外提供统一的遍历接口时。
迭代器模式是一种常见的设计模式,它体现了将数据遍历功能与存储数据的形式相隔离的思想。目前迭代器已经成了操作集合的基本工具,Java的容器类大部分都已经实现了这个迭代器接口。
标签:Iterator,迭代,Pattern,ArrayList,public,cursor,new,啥样,lastRet From: https://blog.csdn.net/Zongkunxue/article/details/144918463