Skip to content

迭代器模式 (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));
}

问题:

  1. 客户端需要了解不同集合的内部结构
  2. 遍历代码与集合类型耦合
  3. 无法统一处理不同类型的集合

三、结构

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());
    }
}

五、优缺点

优点

  1. 统一遍历接口:客户端以相同方式遍历不同集合
  2. 简化集合接口:遍历逻辑从集合中分离,集合接口更简洁
  3. 支持多种遍历:同一集合可以有多个迭代器,支持不同的遍历策略
  4. 封装内部实现:客户端不需要知道集合的底层数据结构

缺点

  1. 类数量增加:每个集合都需要对应迭代器
  2. 简单遍历过度设计:对于简单集合,直接使用 for 循环更简洁
  3. 并发修改问题:迭代过程中集合被修改会导致 ConcurrentModificationException

六、适用场景

  1. 遍历复杂数据结构:树、图等非线性结构的遍历
  2. 统一遍历接口:需要统一处理不同类型集合的场景
  3. 多种遍历方式:正序遍历、逆序遍历、中序遍历等
  4. 大数据量分批处理:分页迭代器、流式处理

七、JDK / Spring 框架中的实际应用

框架应用位置说明
JDKjava.util.Iterator迭代器接口
JDKjava.util.Enumeration老式迭代器
JDKjava.util.ListIterator双向迭代器
JDKjava.util.Spliterator可分割迭代器,支持并行流
SpringCompositeIterator组合多个迭代器
MyBatisCursor数据库游标迭代器,流式读取

八、与其他模式的关系

与组合模式

  • 迭代器常用于遍历组合模式中的树形结构

与工厂方法模式

  • 聚合对象中的 iterator() 方法本质上是工厂方法模式

与备忘录模式

  • 迭代器的状态(当前位置)可以视为备忘录,支持暂停和恢复遍历

与访问者模式

  • 访问者模式可以替代迭代器处理复杂对象的遍历

九、面试常见问题

Q1:Java 中 fail-fast 和 fail-safe 迭代器有什么区别?

A:fail-fast 迭代器(如 ArrayList、HashMap 的迭代器)在遍历过程中检测到集合被修改时会立即抛出 ConcurrentModificationException。fail-safe 迭代器(如 CopyOnWriteArrayListConcurrentHashMap 的迭代器)遍历的是原始集合的快照,不会抛出异常,但可能无法反映最新数据。

Q2:增强 for 循环(for-each)和迭代器有什么关系?

A:增强 for 循环是 Java 5 引入的语法糖,编译后会转换为迭代器形式。使用的对象必须实现 Iterable 接口。增强 for 循环不支持在遍历过程中删除元素,而迭代器支持 remove()

Q3:IteratorListIterator 有什么区别?

A:Iterator 只能单向遍历,支持 hasNext()next()remove()ListIterator 继承 Iterator,支持双向遍历(hasPrevious()previous()),还支持 add()set() 方法,可以在遍历时添加或修改元素。

Q4:什么情况下应该自定义迭代器而不是使用 JDK 提供的?

A:当需要遍历非标准数据结构(如树、图)时,或者需要实现特殊的遍历策略(如分页遍历、过滤遍历、懒加载遍历)时,应该自定义迭代器。