Appearance
迭代器模式 (Iterator)
一、定义
一句话概括:提供一种方法顺序访问集合对象的各个元素,而又不暴露其内部表示。
官方定义:Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
二、解决的问题
2.1 问题场景
在开发中,需要遍历不同类型的集合:数组、ArrayList、LinkedList、HashSet、TreeSet 等。如果每个集合都使用不同的遍历方式,客户端代码需要知道每种集合的内部实现。
2.2 不用迭代器模式会怎样?
java
// 反例:不同集合需要不同的遍历方式
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i)); // ArrayList 用索引
}
LinkedList<String> linkedList = new LinkedList<>();
for (int i = 0; i < linkedList.size(); i++) {
// LinkedList 用索引遍历效率低(O(n²))
System.out.println(linkedList.get(i));
}问题:
- 客户端需要了解不同集合的内部结构
- 遍历代码与集合类型耦合
- 无法统一处理不同类型的集合
三、结构
3.1 角色组成
| 角色 | 说明 |
|---|---|
| Iterator(迭代器接口) | 定义访问和遍历元素的接口 |
| ConcreteIterator(具体迭代器) | 实现迭代器接口,维护遍历的当前位置 |
| Aggregate(聚合接口) | 定义创建迭代器对象的接口 |
| ConcreteAggregate(具体聚合) | 实现创建迭代器接口,返回具体迭代器实例 |
3.2 类图(ASCII)
┌──────────────┐ ┌──────────────┐
│ Aggregate │ │ Iterator │
├──────────────┤ ├──────────────┤
│+ iterator() │────────►│+ hasNext() │
└──────┬───────┘ │+ next() │
│ └──────┬───────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ConcreteAggregate│ │ConcreteIterator│
│ │ │ - index │
│+ iterator() │ │ + hasNext() │
└──────────────┘ │ + next() │
└──────────────┘四、代码实现
4.1 基础实现
java
// ==================== 迭代器接口 ====================
interface Iterator<T> {
boolean hasNext();
T next();
}
// ==================== 聚合接口 ====================
interface Aggregate<T> {
Iterator<T> iterator();
}
// ==================== 书籍类 ====================
class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// ==================== 具体聚合:书籍书架 ====================
class BookShelf implements Aggregate<Book> {
private Book[] books;
private int size = 0;
private static final int MAX = 100;
public BookShelf() {
books = new Book[MAX];
}
public void addBook(Book book) {
if (size < MAX) {
books[size++] = book;
}
}
public Book getBookAt(int index) {
return books[index];
}
public int getLength() {
return size;
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
}
// ==================== 具体迭代器 ====================
class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int index = 0;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
}
@Override
public boolean hasNext() {
return index < bookShelf.getLength();
}
@Override
public Book next() {
return bookShelf.getBookAt(index++);
}
}
// ==================== 客户端 ====================
public class IteratorDemo {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf();
bookShelf.addBook(new Book("设计模式"));
bookShelf.addBook(new Book("重构"));
bookShelf.addBook(new Book("代码整洁之道"));
Iterator<Book> it = bookShelf.iterator();
while (it.hasNext()) {
Book book = it.next();
System.out.println(book.getName());
}
}
}4.2 进阶实现
4.2.1 内部迭代器 vs 外部迭代器
外部迭代器:由客户端控制迭代过程(如上面的示例),灵活性高但客户端需要编写循环逻辑。
内部迭代器:由迭代器自身控制迭代过程,客户端只需传入处理逻辑。
java
// 内部迭代器
interface InternalIterator<T> {
void forEach(Consumer<T> action);
}
class BookShelfWithInternalIterator extends BookShelf {
class InternalIteratorImpl implements InternalIterator<Book> {
@Override
public void forEach(Consumer<Book> action) {
for (int i = 0; i < getLength(); i++) {
action.accept(getBookAt(i));
}
}
}
public InternalIterator<Book> internalIterator() {
return new InternalIteratorImpl();
}
}
// 使用内部迭代器
public class InternalIteratorDemo {
public static void main(String[] args) {
BookShelfWithInternalIterator shelf = new BookShelfWithInternalIterator();
shelf.addBook(new Book("A"));
shelf.addBook(new Book("B"));
// 内部迭代:客户端无需关心遍历逻辑
shelf.internalIterator().forEach(book -> {
System.out.println(book.getName());
});
}
}4.2.2 Java Iterator 源码分析
java
// java.util.Iterator 接口(JDK 源码)
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
// ArrayList 的内部迭代器实现(Itr)
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引
int lastRet = -1; // 上一个返回元素的索引
int expectedModCount = modCount; // fail-fast 机制
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}4.2.3 自定义双向迭代器
java
interface BidirectionalIterator<T> extends Iterator<T> {
boolean hasPrevious();
T previous();
}
class ArrayListBidirectionalIterator<T> implements BidirectionalIterator<T> {
private List<T> list;
private int cursor = 0;
public ArrayListBidirectionalIterator(List<T> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return cursor < list.size();
}
@Override
public T next() {
return list.get(cursor++);
}
@Override
public boolean hasPrevious() {
return cursor > 0;
}
@Override
public T previous() {
return list.get(--cursor);
}
}4.3 生产级实现
Spring Boot 分页迭代器
java
// ==================== 分页迭代器:大数据量分批查询 ====================
class PaginationIterator<T> implements Iterator<List<T>> {
private final Function<PageRequest, PageResult<T>> pageFetcher;
private int currentPage = 0;
private int pageSize;
private PageResult<T> currentResult;
public PaginationIterator(Function<PageRequest, PageResult<T>> pageFetcher, int pageSize) {
this.pageFetcher = pageFetcher;
this.pageSize = pageSize;
}
@Override
public boolean hasNext() {
if (currentResult == null) {
currentResult = pageFetcher.apply(new PageRequest(currentPage, pageSize));
return currentResult != null && !currentResult.getItems().isEmpty();
}
return currentResult != null && currentResult.isHasMore();
}
@Override
public List<T> next() {
if (currentResult == null) {
currentResult = pageFetcher.apply(new PageRequest(currentPage, pageSize));
}
List<T> items = currentResult.getItems();
currentPage++;
currentResult = pageFetcher.apply(new PageRequest(currentPage, pageSize));
return items;
}
}
// ==================== 分页请求/响应 ====================
class PageRequest {
private int page;
private int size;
public PageRequest(int page, int size) { this.page = page; this.size = size; }
public int getPage() { return page; }
public int getSize() { return size; }
}
class PageResult<T> {
private List<T> items;
private boolean hasMore;
public PageResult(List<T> items, boolean hasMore) {
this.items = items;
this.hasMore = hasMore;
}
public List<T> getItems() { return items; }
public boolean isHasMore() { return hasMore; }
}
// ==================== Spring Boot 使用 ====================
@Service
class OrderExportService {
@Autowired
private OrderRepository orderRepository;
public void exportLargeOrders() {
PaginationIterator<Order> iterator = new PaginationIterator<>(
req -> {
Page<Order> page = orderRepository.findAll(
PageRequest.of(req.getPage(), req.getSize()));
return new PageResult<>(page.getContent(), page.hasNext());
},
1000 // 每页 1000 条
);
while (iterator.hasNext()) {
List<Order> batch = iterator.next();
processBatch(batch); // 分批处理
}
}
private void processBatch(List<Order> orders) {
System.out.println("处理批次,大小: " + orders.size());
}
}五、优缺点
优点
- 统一遍历接口:客户端以相同方式遍历不同集合
- 简化集合接口:遍历逻辑从集合中分离,集合接口更简洁
- 支持多种遍历:同一集合可以有多个迭代器,支持不同的遍历策略
- 封装内部实现:客户端不需要知道集合的底层数据结构
缺点
- 类数量增加:每个集合都需要对应迭代器
- 简单遍历过度设计:对于简单集合,直接使用 for 循环更简洁
- 并发修改问题:迭代过程中集合被修改会导致
ConcurrentModificationException
六、适用场景
- 遍历复杂数据结构:树、图等非线性结构的遍历
- 统一遍历接口:需要统一处理不同类型集合的场景
- 多种遍历方式:正序遍历、逆序遍历、中序遍历等
- 大数据量分批处理:分页迭代器、流式处理
七、JDK / Spring 框架中的实际应用
| 框架 | 应用位置 | 说明 |
|---|---|---|
| JDK | java.util.Iterator | 迭代器接口 |
| JDK | java.util.Enumeration | 老式迭代器 |
| JDK | java.util.ListIterator | 双向迭代器 |
| JDK | java.util.Spliterator | 可分割迭代器,支持并行流 |
| Spring | CompositeIterator | 组合多个迭代器 |
| MyBatis | Cursor | 数据库游标迭代器,流式读取 |
八、与其他模式的关系
与组合模式
- 迭代器常用于遍历组合模式中的树形结构
与工厂方法模式
- 聚合对象中的
iterator()方法本质上是工厂方法模式
与备忘录模式
- 迭代器的状态(当前位置)可以视为备忘录,支持暂停和恢复遍历
与访问者模式
- 访问者模式可以替代迭代器处理复杂对象的遍历
九、面试常见问题
Q1:Java 中 fail-fast 和 fail-safe 迭代器有什么区别?
A:fail-fast 迭代器(如 ArrayList、HashMap 的迭代器)在遍历过程中检测到集合被修改时会立即抛出 ConcurrentModificationException。fail-safe 迭代器(如 CopyOnWriteArrayList、ConcurrentHashMap 的迭代器)遍历的是原始集合的快照,不会抛出异常,但可能无法反映最新数据。
Q2:增强 for 循环(for-each)和迭代器有什么关系?
A:增强 for 循环是 Java 5 引入的语法糖,编译后会转换为迭代器形式。使用的对象必须实现 Iterable 接口。增强 for 循环不支持在遍历过程中删除元素,而迭代器支持 remove()。
Q3:Iterator 和 ListIterator 有什么区别?
A:Iterator 只能单向遍历,支持 hasNext()、next()、remove()。ListIterator 继承 Iterator,支持双向遍历(hasPrevious()、previous()),还支持 add()、set() 方法,可以在遍历时添加或修改元素。
Q4:什么情况下应该自定义迭代器而不是使用 JDK 提供的?
A:当需要遍历非标准数据结构(如树、图)时,或者需要实现特殊的遍历策略(如分页遍历、过滤遍历、懒加载遍历)时,应该自定义迭代器。
