行为型模式-迭代器模式

2 阅读53分钟

概述

迭代器模式(Iterator Pattern) 是GoF(Gang of Four)设计模式中最经典的行为型模式之一。它的核心定义是:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。这个看似简单的定义背后,蕴含着一个深刻的设计哲学——将“遍历”这一行为从“聚合”这一数据结构中解耦出来。

在现实开发中,我们经常会遇到这样的困境:聚合对象可能是数组、链表、树、图、哈希表甚至是远程数据流,它们的内部结构千差万别。如果让客户端直接操作这些内部结构进行遍历,代码将与具体的数据结构紧密耦合,任何内部实现的变更都会引发客户端代码的连锁修改。迭代器模式优雅地解决了这一问题:它抽象出一个统一的Iterator接口,封装了遍历算法的状态与逻辑,使得客户端无需关心聚合对象是数组还是链表,只需调用hasNext()next()即可完成遍历。更强大的是,迭代器模式天然支持多种遍历策略——正向、反向、过滤、跳跃——甚至可以同时存在多个迭代器独立地遍历同一个聚合对象。

本文将带领你从Java集合框架的迭代器设计出发,逐步深入到自定义迭代器的实现,再拓展至JDK、Spring、MyBatis等主流框架源码中的迭代器精妙应用,最后探讨分布式环境下如何运用迭代器模式优雅处理海量数据的流式遍历。无论你是准备系统学习设计模式的开发者,还是正在备战技术专家面试的候选人,本文都将为你提供一场关于迭代器模式的深度盛宴。


一、模式定义与结构

1.1 GoF标准定义

迭代器模式(Iterator Pattern):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

1.2 UML类图

classDiagram
    class Iterator {
        <<interface>>
        +hasNext() boolean
        +next() Object
    }

    class ConcreteIterator {
        -aggregate : ConcreteAggregate
        -currentIndex : int
        +hasNext() boolean
        +next() Object
    }

    class Aggregate {
        <<interface>>
        +createIterator() Iterator
    }

    class ConcreteAggregate {
        -items : Object[]
        +createIterator() Iterator
        +getItem(index) Object
        +size() int
    }

    class Client {
        +main(args: String[]) void
    }

    Iterator <|.. ConcreteIterator
    Aggregate <|.. ConcreteAggregate
    ConcreteIterator --> ConcreteAggregate : 持有引用
    Client --> Aggregate
    Client --> Iterator

1.3 模式解耦原理深度解析

迭代器模式的精髓在于它成功地将聚合对象的内部结构遍历算法解耦。在没有迭代器的情况下,客户端要遍历一个聚合对象,必须知道其内部的具体实现方式:如果是数组,需要知道长度并使用下标访问;如果是链表,需要持有头结点并不断next;如果是哈希表,甚至需要先遍历桶数组再遍历链表。这种紧密耦合导致两个严重问题:第一,客户端代码变得脆弱,聚合对象内部结构的任何修改都会迫使所有遍历代码同步修改;第二,遍历算法无法复用,同样的遍历逻辑在不同聚合类中需要重复编写。

引入迭代器模式后,聚合对象(ConcreteAggregate)只负责提供数据的存储和管理,而将“如何遍历自己”这一职责委托给专门的迭代器对象(ConcreteIterator)。聚合对象通过createIterator()工厂方法对外暴露迭代器实例,客户端通过抽象的Iterator接口与迭代器交互,完全不触及聚合对象的内部细节。这种设计使得同一个聚合对象可以同时支持多种遍历策略——例如,一个树形结构可以分别提供前序、中序、后序、层序迭代器,而这些迭代器都实现同一个Iterator接口,客户端代码无需修改即可切换不同的遍历算法。

此外,迭代器模式还天然支持多遍历状态并存。由于每个迭代器对象独立维护自己的游标位置,多个迭代器可以同时遍历同一个聚合对象而互不干扰,这在多线程环境或需要对同一集合进行嵌套遍历时尤为重要。

1.4 角色职责对照

角色职责关键代码要素
Iterator(抽象迭代器)声明遍历接口,定义访问和遍历元素的方法hasNext(), next(), 可选remove()
ConcreteIterator(具体迭代器)实现具体遍历算法,维护遍历过程中的位置状态持有聚合对象引用;维护cursorlastRet等游标变量
Aggregate(抽象聚合)声明创建迭代器对象的接口createIterator()
ConcreteAggregate(具体聚合)实现createIterator(),返回能够遍历自身元素的迭代器实例内部数据结构(如数组、链表头);createIterator()返回对应的ConcreteIterator实例
Client(客户端)通过AggregateIterator接口与聚合对象及迭代器交互调用aggregate.createIterator()获取迭代器,通过while(iterator.hasNext())循环遍历

二、代码演进与实现

2.1 不使用模式的原始代码

让我们从一个简单的场景开始:一个BookShelf类(书架),内部使用数组存储书籍。客户端需要遍历书架中的所有书籍。

// 原始聚合类:内部数组直接暴露给客户端
class BookShelf {
    private Book[] books;
    private int count;
    
    public BookShelf(int capacity) {
        books = new Book[capacity];
        count = 0;
    }
    
    public void addBook(Book book) {
        if (count < books.length) {
            books[count++] = book;
        }
    }
    
    // 问题1:暴露内部数组
    public Book[] getBooks() {
        return books;
    }
    
    // 问题2:暴露内部计数
    public int getCount() {
        return count;
    }
}

class Book {
    private String title;
    public Book(String title) { this.title = title; }
    public String getTitle() { return title; }
}

// 客户端遍历代码
public class ClientWithoutIterator {
    public static void main(String[] args) {
        BookShelf shelf = new BookShelf(5);
        shelf.addBook(new Book("Design Patterns"));
        shelf.addBook(new Book("Effective Java"));
        shelf.addBook(new Book("Clean Code"));
        
        // 直接依赖BookShelf的内部数组结构进行遍历
        Book[] books = shelf.getBooks();
        int count = shelf.getCount();
        for (int i = 0; i < count; i++) {
            System.out.println(books[i].getTitle());
        }
    }
}

问题分析

  1. 暴露内部表示:客户端必须知道BookShelf使用数组存储,并直接访问books数组。
  2. 遍历逻辑无法复用:如果将来BookShelf改用ArrayList或链表实现,所有客户端遍历代码都必须修改。
  3. 缺少遍历控制:客户端无法暂停、恢复遍历,也无法支持多种遍历顺序。
  4. 并发安全性缺失:遍历期间如果BookShelf被修改,客户端无法感知。

2.2 经典迭代器模式重构

2.2.1 定义迭代器接口

/**
 * 抽象迭代器接口
 * 定义了遍历聚合对象元素的标准方法
 */
interface Iterator<T> {
    /**
     * 判断是否还有下一个元素
     * @return true表示仍有元素可遍历
     */
    boolean hasNext();
    
    /**
     * 获取下一个元素,并将游标移动到下一位
     * @return 下一个元素
     * @throws NoSuchElementException 当没有更多元素时抛出
     */
    T next();
}

2.2.2 实现具体迭代器类

import java.util.NoSuchElementException;

/**
 * 针对数组聚合的具体迭代器实现
 * 维护当前遍历位置(游标cursor)
 */
class ArrayIterator<T> implements Iterator<T> {
    private T[] items;          // 持有聚合对象的引用
    private int cursor = 0;     // 当前位置游标,指向下一个待返回元素的索引
    private int size;           // 聚合对象实际元素数量
    
    public ArrayIterator(T[] items, int size) {
        this.items = items;
        this.size = size;
    }
    
    @Override
    public boolean hasNext() {
        // 当游标未超过实际元素数量时,表示还有元素
        return cursor < size;
    }
    
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        // 返回当前元素,并移动游标
        return items[cursor++];
    }
}

/**
 * 针对链表聚合的具体迭代器实现
 * 维护当前节点的引用
 */
class LinkedListIterator<T> implements Iterator<T> {
    private Node<T> currentNode;  // 当前节点引用
    
    public LinkedListIterator(Node<T> head) {
        this.currentNode = head;
    }
    
    @Override
    public boolean hasNext() {
        return currentNode != null;
    }
    
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        T data = currentNode.data;
        currentNode = currentNode.next;  // 移动到下一个节点
        return data;
    }
}

// 链表节点定义
class Node<T> {
    T data;
    Node<T> next;
    Node(T data) { this.data = data; }
}

2.2.3 定义聚合接口

/**
 * 抽象聚合接口
 * 所有可被遍历的聚合对象都应实现此接口
 */
interface Aggregate<T> {
    /**
     * 创建并返回一个用于遍历当前聚合对象的迭代器
     * @return 迭代器实例
     */
    Iterator<T> createIterator();
}

2.2.4 实现具体聚合类

/**
 * 基于数组的聚合实现
 */
class ArrayAggregate<T> implements Aggregate<T> {
    private T[] items;
    private int size;
    
    @SuppressWarnings("unchecked")
    public ArrayAggregate(int capacity) {
        items = (T[]) new Object[capacity];
        size = 0;
    }
    
    public void add(T item) {
        if (size < items.length) {
            items[size++] = item;
        }
    }
    
    public int size() {
        return size;
    }
    
    // 用于迭代器内部访问元素(包级私有访问)
    T getItem(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        return items[index];
    }
    
    @Override
    public Iterator<T> createIterator() {
        // 返回专门用于数组的迭代器
        return new ArrayIterator<>(items, size);
    }
}

/**
 * 基于链表的聚合实现
 */
class LinkedListAggregate<T> implements Aggregate<T> {
    private Node<T> head;
    private Node<T> tail;
    private int size;
    
    public LinkedListAggregate() {
        head = null;
        tail = null;
        size = 0;
    }
    
    public void add(T item) {
        Node<T> newNode = new Node<>(item);
        if (head == null) {
            head = tail = newNode;
        } else {
            tail.next = newNode;
            tail = newNode;
        }
        size++;
    }
    
    public int size() {
        return size;
    }
    
    @Override
    public Iterator<T> createIterator() {
        // 返回专门用于链表的迭代器
        return new LinkedListIterator<>(head);
    }
}

2.2.5 客户端统一遍历

public class IteratorPatternDemo {
    public static void main(String[] args) {
        // 使用数组聚合
        ArrayAggregate<Book> arrayShelf = new ArrayAggregate<>(5);
        arrayShelf.add(new Book("Design Patterns"));
        arrayShelf.add(new Book("Effective Java"));
        arrayShelf.add(new Book("Clean Code"));
        
        System.out.println("=== 遍历数组聚合 ===");
        traverseAndPrint(arrayShelf);
        
        // 使用链表聚合
        LinkedListAggregate<Book> linkedShelf = new LinkedListAggregate<>();
        linkedShelf.add(new Book("Domain-Driven Design"));
        linkedShelf.add(new Book("Refactoring"));
        linkedShelf.add(new Book("Patterns of Enterprise Application Architecture"));
        
        System.out.println("\n=== 遍历链表聚合 ===");
        traverseAndPrint(linkedShelf);
    }
    
    /**
     * 通用的遍历方法——完全依赖于接口,与具体聚合实现解耦
     */
    private static <T> void traverseAndPrint(Aggregate<T> aggregate) {
        Iterator<T> iterator = aggregate.createIterator();
        while (iterator.hasNext()) {
            T item = iterator.next();
            System.out.println(item);
        }
    }
}

class Book {
    private String title;
    public Book(String title) { this.title = title; }
    @Override
    public String toString() { return title; }
}

2.3 迭代器模式的进阶特性

2.3.1 支持多种遍历策略

我们可以为同一个聚合对象提供多种迭代器,每种迭代器封装不同的遍历策略。以下示例展示了正向、反向和过滤迭代器。

/**
 * 增强版聚合接口:支持多种迭代器创建
 */
interface AdvancedAggregate<T> extends Aggregate<T> {
    Iterator<T> createReverseIterator();           // 反向迭代器
    Iterator<T> createFilterIterator(Predicate<T> filter);  // 过滤迭代器
}

/**
 * 反向迭代器实现
 */
class ReverseArrayIterator<T> implements Iterator<T> {
    private T[] items;
    private int cursor;  // 从末尾开始向前移动
    private int size;
    
    public ReverseArrayIterator(T[] items, int size) {
        this.items = items;
        this.size = size;
        this.cursor = size - 1;  // 指向最后一个有效元素
    }
    
    @Override
    public boolean hasNext() {
        return cursor >= 0;
    }
    
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return items[cursor--];
    }
}

/**
 * 过滤迭代器:只返回满足条件的元素
 */
class FilterArrayIterator<T> implements Iterator<T> {
    private T[] items;
    private int size;
    private int cursor;
    private Predicate<T> filter;
    private T nextItem;  // 缓存下一个符合条件的元素
    private boolean hasNext;  // 预取标志
    
    public FilterArrayIterator(T[] items, int size, Predicate<T> filter) {
        this.items = items;
        this.size = size;
        this.filter = filter;
        this.cursor = 0;
        advanceToNextValid();  // 初始化时找到第一个符合条件的元素
    }
    
    /**
     * 移动游标直到找到下一个满足条件的元素
     */
    private void advanceToNextValid() {
        hasNext = false;
        while (cursor < size) {
            T candidate = items[cursor++];
            if (filter.test(candidate)) {
                nextItem = candidate;
                hasNext = true;
                break;
            }
        }
    }
    
    @Override
    public boolean hasNext() {
        return hasNext;
    }
    
    @Override
    public T next() {
        if (!hasNext) {
            throw new NoSuchElementException();
        }
        T result = nextItem;
        advanceToNextValid();  // 预取下一个符合条件的元素
        return result;
    }
}

/**
 * 实现AdvancedAggregate的具体聚合类
 */
class EnhancedArrayAggregate<T> extends ArrayAggregate<T> implements AdvancedAggregate<T> {
    public EnhancedArrayAggregate(int capacity) {
        super(capacity);
    }
    
    @Override
    public Iterator<T> createReverseIterator() {
        return new ReverseArrayIterator<>(getItemsArray(), size());
    }
    
    @Override
    public Iterator<T> createFilterIterator(Predicate<T> filter) {
        return new FilterArrayIterator<>(getItemsArray(), size(), filter);
    }
    
    // 辅助方法:暴露内部数组供迭代器使用(包私有)
    @SuppressWarnings("unchecked")
    T[] getItemsArray() {
        // 通过反射获取父类私有字段,此处简化实现
        // 实际开发中应使用更优雅的设计
        return (T[]) new Object[0]; // 占位,实际应正确实现
    }
}

// 客户端使用示例
public class MultiStrategyDemo {
    public static void main(String[] args) {
        EnhancedArrayAggregate<Integer> numbers = new EnhancedArrayAggregate<>(10);
        numbers.add(1); numbers.add(2); numbers.add(3); 
        numbers.add(4); numbers.add(5); numbers.add(6);
        
        System.out.print("正向遍历: ");
        Iterator<Integer> forward = numbers.createIterator();
        while (forward.hasNext()) System.out.print(forward.next() + " ");
        
        System.out.print("\n反向遍历: ");
        Iterator<Integer> reverse = numbers.createReverseIterator();
        while (reverse.hasNext()) System.out.print(reverse.next() + " ");
        
        System.out.print("\n过滤(偶数): ");
        Iterator<Integer> evenFilter = numbers.createFilterIterator(n -> n % 2 == 0);
        while (evenFilter.hasNext()) System.out.print(evenFilter.next() + " ");
    }
}

2.3.2 外部迭代器 vs 内部迭代器

特性外部迭代器(External Iterator)内部迭代器(Internal Iterator)
控制权客户端显式控制迭代过程聚合对象或迭代器内部控制迭代过程
代表实现JDK IteratorListIteratorJava 8 Stream.forEach()Iterable.forEach()
遍历逻辑客户端编写while(hasNext())循环传入一个Consumer或回调函数
代码风格命令式、显式循环声明式、函数式
中断能力可以在任意时刻break通常难以中断(除非抛出异常)
适用场景需要精细控制遍历过程、需要提前退出简单的全量遍历、并行处理
// 外部迭代器示例(JDK Iterator)
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String s = iter.next();
    if (s.equals("B")) break;  // 可以提前退出
    System.out.println(s);
}

// 内部迭代器示例(Java 8 Stream)
list.stream().forEach(s -> {
    // 无法使用break提前退出
    System.out.println(s);
});

// 内部迭代器的另一种形式(Iterable.forEach)
list.forEach(System.out::println);

2.3.3 快速失败机制(fail-fast)实现原理

快速失败机制是JDK集合框架迭代器的重要特性。其核心是通过一个modCount计数器来检测迭代期间集合是否被结构性修改。

/**
 * 演示fail-fast机制的简化版ArrayList迭代器
 */
class FailFastArrayList<T> implements Aggregate<T> {
    private Object[] elements;
    private int size;
    protected int modCount = 0;  // 结构性修改计数器
    
    public FailFastArrayList() {
        elements = new Object[10];
        size = 0;
    }
    
    public boolean add(T e) {
        elements[size++] = e;
        modCount++;  // 每次添加增加modCount
        return true;
    }
    
    public void remove(int index) {
        System.arraycopy(elements, index + 1, elements, index, size - index - 1);
        elements[--size] = null;
        modCount++;  // 每次删除增加modCount
    }
    
    @Override
    public Iterator<T> createIterator() {
        return new Itr();
    }
    
    /**
     * 内部迭代器实现,支持fail-fast
     */
    private class Itr implements Iterator<T> {
        int cursor = 0;           // 下一个待返回元素的索引
        int lastRet = -1;         // 最后一次返回元素的索引,-1表示没有
        int expectedModCount = modCount;  // 创建迭代器时记录期望的modCount
        
        @Override
        public boolean hasNext() {
            return cursor < size;
        }
        
        @Override
        @SuppressWarnings("unchecked")
        public T next() {
            checkForComodification();  // 每次操作前检查
            if (cursor >= size) {
                throw new NoSuchElementException();
            }
            lastRet = cursor;
            return (T) elements[cursor++];
        }
        
        /**
         * 检查并发修改
         * @throws ConcurrentModificationException 如果modCount被修改
         */
        final void checkForComodification() {
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
        }
    }
}

// 测试fail-fast
public class FailFastDemo {
    public static void main(String[] args) {
        FailFastArrayList<String> list = new FailFastArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        
        Iterator<String> iterator = list.createIterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
            System.out.println(s);
            if (s.equals("B")) {
                list.add("D");  // 迭代过程中修改集合,将抛出ConcurrentModificationException
            }
        }
    }
}

2.4 迭代器模式遍历时序图

sequenceDiagram
    participant Client as 客户端
    participant Aggregate as 具体聚合<br/>(ConcreteAggregate)
    participant Iterator as 具体迭代器<br/>(ConcreteIterator)
    participant InternalData as 内部数据存储
    
    Client->>Aggregate: createIterator()
    Aggregate->>Iterator: new ConcreteIterator(this)
    Iterator-->>Aggregate: 返回迭代器实例
    Aggregate-->>Client: 返回迭代器引用
    
    loop 遍历循环
        Client->>Iterator: hasNext()
        Iterator->>InternalData: 检查是否还有元素
        InternalData-->>Iterator: true/false
        Iterator-->>Client: true/false
        
        alt 有下一个元素
            Client->>Iterator: next()
            Iterator->>InternalData: 获取当前元素
            InternalData-->>Iterator: 元素数据
            Iterator->>Iterator: 移动游标到下一位置
            Iterator-->>Client: 返回元素
        end
    end

时序图说明: 上图详细描绘了迭代器模式的核心交互流程。流程始于客户端向聚合对象请求创建迭代器(createIterator()),聚合对象实例化一个具体迭代器并传入自身的引用(this),随后将迭代器返回给客户端。在遍历循环中,客户端首先调用hasNext()查询是否还有元素,迭代器会检查其内部游标或引用是否指向有效位置,并将布尔结果返回。若存在下一个元素,客户端调用next()获取该元素,迭代器在返回元素的同时负责更新自身的遍历状态(如游标递增或节点引用后移)。值得注意的是,在整个交互过程中,客户端从未直接接触聚合对象的内部数据结构,所有访问都通过迭代器这一抽象层进行。这种设计使得客户端代码与聚合对象的具体实现完全解耦,同时允许每个迭代器独立维护其遍历状态,从而支持对同一聚合对象的并发多路遍历。


三、源码级应用分析

3.1 JDK集合框架深度剖析

3.1.1 Iterator与Iterable接口的设计

在JDK中,迭代器模式被发挥到了极致。java.util.Iteratorjava.lang.Iterable两个接口的分工设计堪称典范:

// java.util.Iterator (JDK 8)
public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() { throw new UnsupportedOperationException("remove"); }
    default void forEachRemaining(Consumer<? super E> action) { ... }
}

// java.lang.Iterable (JDK 8)
public interface Iterable<T> {
    Iterator<T> iterator();
    default void forEach(Consumer<? super T> action) { ... }
    default Spliterator<T> spliterator() { ... }
}

为何实现Iterable才能使用增强for循环? 增强for循环(for (T item : collection))是Java 5引入的语法糖,编译器在处理时会将其翻译为对Iterable.iterator()的调用。任何实现了Iterable接口的类都声明自己“可以被迭代”,编译器便能自动生成基于Iterator的遍历代码。这种设计体现了“面向接口编程”的原则:Iterable定义了聚合对象的可迭代能力,而Iterator定义了迭代器本身的行为。

3.1.2 ArrayList.Itr迭代器内部实现

ArrayList的内部迭代器Itr是理解fail-fast机制的绝佳范例。

// ArrayList.Itr 源码片段(简化)
private class Itr implements Iterator<E> {
    int cursor;       // 下一个元素的索引
    int lastRet = -1; // 最后返回元素的索引
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    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];
    }

    public void remove() {
        if (lastRet < 0) throw new IllegalStateException();
        checkForComodification();
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;  // 关键:迭代器自己的remove操作会同步expectedModCount
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

并发检测机制详解

  • modCountAbstractList中定义的字段,记录列表结构被修改的次数(addremove等操作都会使其递增)。
  • expectedModCount在迭代器创建时被初始化为modCount的当前值。
  • 每次调用next()remove()时,都会先执行checkForComodification()比较二者是否相等。
  • 迭代器自身的remove()方法在删除元素后会更新expectedModCount = modCount,确保后续操作合法。
  • 若在迭代期间,外部直接调用了列表的修改方法,modCount会递增而与expectedModCount不一致,从而抛出ConcurrentModificationException

3.1.3 LinkedList.ListItr双向迭代器

LinkedList实现了ListIterator接口,提供了比Iterator更强大的双向遍历能力。

// ListIterator 接口
public interface ListIterator<E> extends Iterator<E> {
    boolean hasPrevious();
    E previous();
    int nextIndex();
    int previousIndex();
    void set(E e);
    void add(E e);
}

// LinkedList.ListItr 关键实现
private class ListItr implements ListIterator<E> {
    private Node<E> lastReturned;
    private Node<E> next;
    private int nextIndex;
    private int expectedModCount = modCount;

    ListItr(int index) {
        next = (index == size) ? null : node(index);
        nextIndex = index;
    }

    public boolean hasNext() { return nextIndex < size; }
    public E next() { ... }
    public boolean hasPrevious() { return nextIndex > 0; }
    
    public E previous() {
        checkForComodification();
        if (!hasPrevious()) throw new NoSuchElementException();
        // 通过previous()可以往回遍历
        lastReturned = next = (next == null) ? last : next.prev;
        nextIndex--;
        return lastReturned.item;
    }

    public int nextIndex() { return nextIndex; }
    public int previousIndex() { return nextIndex - 1; }
    ...
}

ListIterator允许开发者在遍历过程中向前、向后移动,并可以在任意位置添加或修改元素,大大增强了遍历的灵活性。

3.1.4 HashMap的迭代器框架

HashMap使用了一个抽象的内部类HashIterator作为所有迭代器(KeyIteratorValueIteratorEntryIterator)的基类,实现了遍历逻辑的复用。

abstract class HashIterator {
    Node<K,V> next;        // 下一个待返回的节点
    Node<K,V> current;     // 当前返回的节点
    int expectedModCount;  // fail-fast
    int index;             // 当前桶索引

    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        // 初始化找到第一个非空桶
        if (t != null && size > 0) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next;
        if (modCount != expectedModCount) throw new ConcurrentModificationException();
        if (e == null) throw new NoSuchElementException();
        // 先尝试在同一链表上移动
        if ((next = (current = e).next) == null && (t = table) != null) {
            // 链表末尾,查找下一个非空桶
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }
}

final class KeyIterator extends HashIterator implements Iterator<K> {
    public final K next() { return nextNode().key; }
}

final class ValueIterator extends HashIterator implements Iterator<V> {
    public final V next() { return nextNode().value; }
}

final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

这种设计体现了模板方法模式迭代器模式的结合:HashIterator定义了遍历哈希表结构的通用算法骨架(处理桶数组和链表),而具体的子类只需实现如何从节点中提取所需数据(键、值或整个条目)。

3.1.5 CopyOnWriteArrayList的COWIterator

CopyOnWriteArrayList的迭代器是典型的安全失败(fail-safe)实现,也称为快照迭代器

static final class COWIterator<E> implements ListIterator<E> {
    private final Object[] snapshot;  // 数组快照
    private int cursor;

    private COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;  // 直接持有创建时的数组引用
    }

    public boolean hasNext() { return cursor < snapshot.length; }
    public boolean hasPrevious() { return cursor > 0; }

    @SuppressWarnings("unchecked")
    public E next() {
        if (!hasNext()) throw new NoSuchElementException();
        return (E) snapshot[cursor++];
    }

    // 不支持修改操作
    public void remove() { throw new UnsupportedOperationException(); }
    public void set(E e) { throw new UnsupportedOperationException(); }
    public void add(E e) { throw new UnsupportedOperationException(); }
}

弱一致性原理

  • 迭代器创建时,CopyOnWriteArrayList将当前的内部数组引用直接传递给COWIterator作为快照。
  • 迭代期间即使列表被修改,修改操作会在底层数组的一个新副本上进行,迭代器持有的快照不会受到影响。
  • 因此迭代器不会抛出ConcurrentModificationException,但它看到的数据可能不是最新的(弱一致性)。
  • 这种设计非常适合读多写少的并发场景,以空间换时间,避免了迭代期间的加锁开销。

3.2 Spring框架深度剖析

3.2.1 CompositeIterator:组合多个迭代器

Spring的CompositeIterator用于将多个迭代器合并为一个统一的迭代器,常用于需要遍历多个来源的数据。

// org.springframework.util.CompositeIterator
public class CompositeIterator<E> implements Iterator<E> {
    private final Set<Iterator<E>> iterators = new LinkedHashSet<>();
    private boolean inUse = false;
    
    public void add(Iterator<E> iterator) {
        Assert.state(!inUse, "You can no longer add iterators to a composite iterator that's already in use");
        if (iterators.contains(iterator)) {
            throw new IllegalArgumentException("You cannot add the same iterator twice");
        }
        iterators.add(iterator);
    }
    
    @Override
    public boolean hasNext() {
        inUse = true;
        // 遍历迭代器集合,找到第一个还有元素的迭代器
        for (Iterator<E> iterator : iterators) {
            if (iterator.hasNext()) {
                return true;
            }
        }
        return false;
    }
    
    @Override
    public E next() {
        inUse = true;
        for (Iterator<E> iterator : iterators) {
            if (iterator.hasNext()) {
                return iterator.next();
            }
        }
        throw new NoSuchElementException("All iterators exhausted");
    }
    
    @Override
    public void remove() {
        throw new UnsupportedOperationException("CompositeIterator does not support remove()");
    }
}

应用场景示例:在Spring MVC中,HandlerMapping可能有多个实现,CompositeIterator可用于合并来自不同HandlerMapping的匹配结果。

3.2.2 IteratorFactoryBean

Spring的IteratorFactoryBean是一个工厂Bean,用于将Iterator暴露为Spring容器中的Bean,便于依赖注入。

// org.springframework.beans.factory.config.IteratorFactoryBean
public class IteratorFactoryBean implements FactoryBean<Iterator<Object>> {
    private Collection<?> sourceCollection;
    
    public void setSourceCollection(Collection<?> sourceCollection) {
        this.sourceCollection = sourceCollection;
    }
    
    @Override
    public Iterator<Object> getObject() {
        if (this.sourceCollection == null) {
            throw new IllegalArgumentException("sourceCollection is required");
        }
        // 每次调用getObject()返回一个新的迭代器
        return (Iterator<Object>) this.sourceCollection.iterator();
    }
    
    @Override
    public Class<?> getObjectType() {
        return Iterator.class;
    }
    
    @Override
    public boolean isSingleton() {
        return false;  // 每次返回新的迭代器实例
    }
}

这使得在Spring Integration等场景中,可以方便地将迭代器作为消息源进行配置。

3.2.3 ResourcePatternResolver中的Resource迭代

Spring的PathMatchingResourcePatternResolver使用迭代器模式来遍历classpath下符合特定模式的所有资源。

// 核心方法:findPathMatchingResources
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
    // ... 解析模式,确定根目录
    String rootDirPath = determineRootDir(locationPattern);
    String subPattern = locationPattern.substring(rootDirPath.length());
    Resource[] rootDirResources = getResources(rootDirPath);
    
    Set<Resource> result = new LinkedHashSet<>(16);
    for (Resource rootDirResource : rootDirResources) {
        // 对每个根目录资源进行递归遍历(内部使用迭代器)
        result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
    }
    return result.toArray(new Resource[0]);
}

doFindMatchingFileResources内部,Spring会创建自定义的迭代器来递归遍历目录树,并根据Ant路径模式进行过滤,最终将匹配的资源收集返回。这种设计使得客户端可以一次性获取所有匹配资源,而内部使用迭代器避免了将所有文件一次性加载到内存。

3.3 MyBatis框架:Cursor接口

MyBatis的Cursor接口是迭代器模式在处理数据库大量查询结果时的典型应用。

// org.apache.ibatis.cursor.Cursor
public interface Cursor<T> extends Closeable, Iterable<T> {
    boolean isOpen();
    boolean isConsumed();
    int getCurrentIndex();
}

// 使用示例
try (Cursor<Employee> cursor = sqlSession.selectCursor("selectEmployees")) {
    for (Employee emp : cursor) {
        process(emp);
    }
}

DefaultCursor的实现巧妙地结合了JDBC的ResultSet

public class DefaultCursor<T> implements Cursor<T> {
    private final ResultSetHandler<T> resultSetHandler;
    private final Statement statement;
    private final ResultSet rs;
    private T current;
    private Status status = Status.CREATED;
    private int index = -1;
    
    // 迭代器实现
    private final CursorIterator cursorIterator = new CursorIterator();
    
    @Override
    public Iterator<T> iterator() {
        if (status == Status.CLOSED) throw new IllegalStateException("Cursor is closed");
        if (status == Status.CREATED) status = Status.OPEN;
        return cursorIterator;
    }
    
    private class CursorIterator implements Iterator<T> {
        // 预取下一个对象,用于hasNext判断
        boolean objectReceived;
        
        @Override
        public boolean hasNext() {
            if (!objectReceived) fetchNextObject();
            return objectReceived;
        }
        
        @Override
        public T next() {
            if (!hasNext()) throw new NoSuchElementException();
            objectReceived = false;
            return current;
        }
        
        private void fetchNextObject() {
            // 通过ResultSet获取下一行并映射为对象
            if (rs.next()) {
                current = resultSetHandler.handleRowValues(rs);
                index++;
                objectReceived = true;
            } else {
                close(); // 自动关闭资源
            }
        }
    }
}

流式迭代的优势

  • 内存效率:逐行读取和处理,避免了将整个结果集加载到List中可能导致的内存溢出。
  • 响应速度:可以尽快开始处理第一行数据,而不必等待全部数据加载完成。
  • 资源管理Cursor实现了Closeable接口,确保底层ResultSet和数据库连接被正确释放。

3.4 其他框架

3.4.1 Java 8 Stream与Spliterator

Spliterator(可分割迭代器)是Java 8引入的并行迭代支持接口。

public interface Spliterator<T> {
    boolean tryAdvance(Consumer<? super T> action);
    Spliterator<T> trySplit();
    long estimateSize();
    int characteristics();
}

与Iterator的区别

  • Iterator只支持顺序遍历,Spliterator支持将数据源分割成多个部分,以便并行处理。
  • trySplit()方法尝试将当前元素集合一分为二,返回新的Spliterator覆盖前半部分,原Spliterator覆盖后半部分。
  • 这是Java并行流(parallelStream())能够高效工作的基础。

3.4.2 Guava的增强迭代器

Google Guava提供了许多实用的迭代器增强工具:

// AbstractIterator:简化自定义迭代器实现
AbstractIterator<String> iterator = new AbstractIterator<String>() {
    private int count = 0;
    @Override
    protected String computeNext() {
        if (count < 10) {
            return "Item" + count++;
        }
        return endOfData();  // 标记结束
    }
};

// PeekingIterator:允许预览下一个元素而不消耗它
PeekingIterator<String> peekingIter = Iterators.peekingIterator(list.iterator());
while (peekingIter.hasNext()) {
    String current = peekingIter.next();
    if (peekingIter.hasNext() && peekingIter.peek().equals(current)) {
        // 处理重复元素
    }
}

四、分布式环境下的迭代器模式

在分布式系统中,数据往往分布存储于多个节点,无法像单机集合那样一次性全部加载到内存。迭代器模式的“顺序访问而不暴露内部表示”的思想,恰好为解决分布式大数据集的遍历提供了优雅的抽象。

4.1 分页查询与游标分页的迭代器封装

在分布式数据库(如MySQL分库分表、MongoDB、Elasticsearch)中,分页查询通常使用LIMIT offset, size,但深分页性能极差。更好的方案是使用游标分页(Cursor Pagination),将迭代器模式应用于分页API的封装。

/**
 * 游标分页迭代器的通用接口
 */
public interface PageIterator<T> extends Iterator<T> {
    // 继承hasNext()和next()
}

/**
 * 基于游标的分页迭代器抽象实现
 */
public abstract class AbstractCursorPageIterator<T> implements PageIterator<T> {
    private Queue<T> buffer = new LinkedList<>();  // 当前页缓存
    private String nextCursor;                     // 下一页游标
    private boolean exhausted = false;             // 是否已遍历完
    
    public AbstractCursorPageIterator(String initialCursor) {
        this.nextCursor = initialCursor;
    }
    
    @Override
    public boolean hasNext() {
        if (!buffer.isEmpty()) {
            return true;
        }
        if (exhausted) {
            return false;
        }
        // 拉取下一页
        Page<T> page = fetchPage(nextCursor);
        buffer.addAll(page.getItems());
        nextCursor = page.getNextCursor();
        exhausted = page.isLast();
        return !buffer.isEmpty();
    }
    
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return buffer.poll();
    }
    
    /**
     * 子类实现具体的分页拉取逻辑
     * @param cursor 当前游标,null表示第一页
     * @return 分页数据
     */
    protected abstract Page<T> fetchPage(String cursor);
    
    public static class Page<T> {
        private List<T> items;
        private String nextCursor;
        private boolean last;
        // 构造器和getter/setter省略
    }
}

4.2 Redis SCAN命令的迭代器封装

Redis的SCAN命令提供了基于游标的迭代方式遍历键空间,完美契合迭代器模式。

import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;

/**
 * Redis SCAN命令的迭代器封装
 */
public class RedisScanIterator implements Iterator<String> {
    private final Jedis jedis;
    private final String pattern;
    private final int batchSize;
    
    private String cursor = ScanParams.SCAN_POINTER_START;  // 初始游标为"0"
    private List<String> currentBatch = new ArrayList<>();
    private int batchIndex = 0;
    private boolean finished = false;
    
    public RedisScanIterator(Jedis jedis, String pattern, int batchSize) {
        this.jedis = jedis;
        this.pattern = pattern;
        this.batchSize = batchSize;
    }
    
    @Override
    public boolean hasNext() {
        // 当前批次还有元素
        if (batchIndex < currentBatch.size()) {
            return true;
        }
        // 已经完成全部扫描
        if (finished) {
            return false;
        }
        // 拉取下一批
        fetchNextBatch();
        return batchIndex < currentBatch.size();
    }
    
    @Override
    public String next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return currentBatch.get(batchIndex++);
    }
    
    private void fetchNextBatch() {
        ScanParams params = new ScanParams()
                .count(batchSize)
                .match(pattern);
        
        ScanResult<String> result = jedis.scan(cursor, params);
        cursor = result.getCursor();
        currentBatch = result.getResult();
        batchIndex = 0;
        
        // 当游标回到"0"时,表示遍历完成
        if ("0".equals(cursor)) {
            finished = true;
        }
    }
    
    // 客户端使用示例
    public static void main(String[] args) {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            RedisScanIterator iterator = new RedisScanIterator(jedis, "user:*", 100);
            while (iterator.hasNext()) {
                String key = iterator.next();
                System.out.println("Processing key: " + key);
            }
        }
    }
}

4.3 分布式消息队列的消费者迭代器

Kafka消费者Consumer.poll()的设计本质上是迭代器模式的一种变体。

// Kafka消费者伪代码
public class KafkaIteratorWrapper<K, V> implements Iterator<ConsumerRecord<K, V>> {
    private final KafkaConsumer<K, V> consumer;
    private final Duration timeout;
    private Iterator<ConsumerRecord<K, V>> currentBatch = Collections.emptyIterator();
    
    public KafkaIteratorWrapper(KafkaConsumer<K, V> consumer, Duration timeout) {
        this.consumer = consumer;
        this.timeout = timeout;
    }
    
    @Override
    public boolean hasNext() {
        if (currentBatch.hasNext()) {
            return true;
        }
        // 拉取下一批消息
        ConsumerRecords<K, V> records = consumer.poll(timeout);
        currentBatch = records.iterator();
        return currentBatch.hasNext();
    }
    
    @Override
    public ConsumerRecord<K, V> next() {
        return currentBatch.next();
    }
    
    // 注意:需要额外提供commitSync等方法
}

4.4 微服务间批量数据同步的远程迭代器

设计一个跨服务的远程迭代器,使客户端能够以统一的迭代器接口逐页拉取远程数据。

/**
 * 远程分页迭代器
 * 封装了对远程服务的分页API调用
 */
public class RemotePageIterator<T> implements Iterator<T> {
    private final RestTemplate restTemplate;
    private final String serviceUrl;
    private final Class<T[]> responseType;
    
    private int currentPage = 0;
    private int pageSize = 100;
    private List<T> buffer = new ArrayList<>();
    private int bufferIndex = 0;
    private boolean exhausted = false;
    
    public RemotePageIterator(RestTemplate restTemplate, String serviceUrl, 
                              Class<T[]> responseType, int pageSize) {
        this.restTemplate = restTemplate;
        this.serviceUrl = serviceUrl;
        this.responseType = responseType;
        this.pageSize = pageSize;
    }
    
    @Override
    public boolean hasNext() {
        if (bufferIndex < buffer.size()) {
            return true;
        }
        if (exhausted) {
            return false;
        }
        fetchNextPage();
        return bufferIndex < buffer.size();
    }
    
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return buffer.get(bufferIndex++);
    }
    
    private void fetchNextPage() {
        String url = String.format("%s?page=%d&size=%d", serviceUrl, currentPage, pageSize);
        ResponseEntity<T[]> response = restTemplate.getForEntity(url, responseType);
        T[] items = response.getBody();
        
        if (items == null || items.length == 0) {
            exhausted = true;
            return;
        }
        
        buffer = Arrays.asList(items);
        bufferIndex = 0;
        currentPage++;
        
        // 如果返回的数据量小于pageSize,说明是最后一页
        if (items.length < pageSize) {
            exhausted = true;
        }
    }
}

4.5 分布式文件系统目录遍历

HDFS中的FileSystem.listStatusIterator就是迭代器模式在海量文件遍历场景的应用。

// HDFS API使用示例
public void processHdfsDirectory(FileSystem fs, Path dirPath) throws IOException {
    RemoteIterator<LocatedFileStatus> iter = fs.listLocatedStatus(dirPath);
    while (iter.hasNext()) {
        LocatedFileStatus status = iter.next();
        if (status.isDirectory()) {
            // 递归处理子目录
            processHdfsDirectory(fs, status.getPath());
        } else {
            // 处理文件
            processFile(status);
        }
    }
}

RemoteIterator的设计避免了将整个目录下的所有文件状态一次性加载到客户端内存,而是采用分批拉取的方式,特别适合包含数百万文件的超大目录遍历场景。

4.6 分布式数据迭代架构图

flowchart TB
    subgraph Client[客户端]
        A[应用代码] --> B[RemoteIterator]
    end
    
    subgraph IteratorInternal[迭代器内部]
        B --> C{本地缓存<br/>是否有数据?}
        C -->|有| D[返回元素]
        C -->|无| E[构建分页请求]
        E --> F[发送RPC/HTTP请求]
    end
    
    subgraph DataSource[数据源]
        F --> G[分页API网关]
        G --> H[(分布式数据库)]
        H --> I[分片1]
        H --> J[分片2]
        H --> K[分片N]
        G --> L[返回批次数据]
    end
    
    L --> M[更新本地缓存和游标]
    M --> C
    D --> N{是否继续?}
    N -->|是| C
    N -->|否| O[结束]

架构图说明: 该流程图展示了分布式环境下远程迭代器的工作机制。客户端代码通过RemoteIterator接口与迭代器交互,完全不必关心数据是来自本地集合还是远程服务。迭代器内部维护了一个本地缓冲区(通常是一页数据)和一个状态游标(如页码或数据库游标)。当hasNext()被调用且本地缓存为空时,迭代器自动发起一次RPC/HTTP请求到数据源的分页API。数据源可能是分布式数据库(如图中的多个分片)、搜索引擎或对象存储服务。分页API返回一小批数据及下一页的游标信息,迭代器将其填充到本地缓存中并更新内部游标。随后客户端调用next()时直接从本地缓存取出元素。当缓存再次耗尽时,迭代器会基于上次返回的游标继续请求下一页,直到返回空数据标记遍历结束。这种设计将网络延迟、分页逻辑和分布式数据源的复杂性完美封装在迭代器内部,为上层应用提供了统一、简洁的流式数据处理接口。


五、对比辨析

5.1 迭代器模式 vs 组合模式

维度迭代器模式组合模式
关注点如何遍历聚合对象中的元素如何构建树形结构的对象层次
核心职责提供统一的遍历接口,隐藏内部结构将对象组合成树形结构以表示“部分-整体”层次
协作关系通常作为组合模式的辅助者,用于遍历组合结构通常被迭代器模式作为遍历对象

深度解析:迭代器模式与组合模式经常配合使用。例如,要遍历一个包含文件夹和文件的树形文件系统(组合模式),我们可以实现一个深度优先的迭代器(迭代器模式)来遍历整个树。组合模式关注的是结构的构建——将叶子对象和组合对象统一对待;而迭代器模式关注的是如何在不暴露树结构细节的情况下,按照特定顺序访问这些节点。两者结合时,迭代器内部通常使用栈或队列来管理遍历状态,对外则表现为一个简单的线性迭代器。

5.2 迭代器模式 vs 访问者模式

维度迭代器模式访问者模式
控制权由客户端控制遍历顺序和时机由对象结构自身控制遍历顺序
操作添加新操作需修改客户端代码新操作只需添加新访问者,符合开闭原则
结构变化聚合对象结构变化不影响迭代器对象结构变化(新增元素类)会导致所有访问者修改
双向依赖迭代器依赖于聚合对象访问者和元素之间是双向依赖

本质区别:迭代器模式中,“遍历”与“操作”是分离的——客户端先获取迭代器,然后在循环内对每个元素执行操作。访问者模式则将“操作”封装在访问者对象中,由元素在遍历过程中“接受”访问者。迭代器更适合客户端需要精细控制遍历过程(如提前终止、条件跳过)的场景;访问者更适合需要对一组稳定的元素类执行多种不同操作,且希望将操作集中管理的场景。

5.3 迭代器模式 vs 观察者模式

  • 迭代器模式:采用拉(Pull)模型。客户端主动调用next()从聚合对象中“拉取”数据,客户端决定何时获取下一个元素。
  • 观察者模式:采用推(Push)模型。当被观察对象状态发生变化时,主动将数据“推送”给所有注册的观察者,观察者被动接收通知。

这一区别决定了它们在并发环境下的适用性:拉模型更适合处理背压(backpressure),因为客户端可以根据自身处理能力控制拉取速度;推模型则可能导致观察者被过量的通知淹没。

5.4 外部迭代器 vs 内部迭代器

对比维度外部迭代器内部迭代器
控制流客户端显式控制迭代循环聚合对象或迭代器内部控制迭代循环
代表实现java.util.IteratorIterable.forEach(Consumer), Stream.forEach
提前退出可以(通过breakreturn困难(需抛出异常)
状态管理迭代器状态暴露给客户端状态隐藏在内部
并行支持需要客户端显式处理内部迭代器更容易支持并行(如parallelStream

5.5 快速失败(fail-fast)vs 安全失败(fail-safe)

特性fail-fast(快速失败)fail-safe(安全失败)
检测机制通过modCount计数器检测并发修改不检测,基于快照遍历
异常行为检测到并发修改立即抛出ConcurrentModificationException不抛出异常,允许遍历期间修改
内存开销低(仅额外计数器)高(需要复制底层数据结构或持有快照)
数据一致性强一致性(但禁止并发修改)弱一致性(可能看到过期数据)
代表实现ArrayList, HashMapCopyOnWriteArrayList, ConcurrentHashMap
适用场景非并发环境,快速暴露bug高并发读多写少场景

六、适用场景分析(重点强化)

场景一:自定义树形结构遍历

Demo:二叉树的前序、中序、后序、层序迭代器

/**
 * 二叉树节点
 */
class TreeNode<T> {
    T value;
    TreeNode<T> left;
    TreeNode<T> right;
    
    public TreeNode(T value) {
        this.value = value;
    }
}

/**
 * 二叉树聚合类,提供多种迭代器
 */
class BinaryTree<T> implements Iterable<T> {
    private TreeNode<T> root;
    
    public BinaryTree(TreeNode<T> root) {
        this.root = root;
    }
    
    // 默认使用中序遍历
    @Override
    public Iterator<T> iterator() {
        return new InOrderIterator<>(root);
    }
    
    public Iterator<T> preOrderIterator() {
        return new PreOrderIterator<>(root);
    }
    
    public Iterator<T> inOrderIterator() {
        return new InOrderIterator<>(root);
    }
    
    public Iterator<T> postOrderIterator() {
        return new PostOrderIterator<>(root);
    }
    
    public Iterator<T> levelOrderIterator() {
        return new LevelOrderIterator<>(root);
    }
}

/**
 * 前序遍历迭代器:根 -> 左 -> 右
 */
class PreOrderIterator<T> implements Iterator<T> {
    private Stack<TreeNode<T>> stack = new Stack<>();
    
    public PreOrderIterator(TreeNode<T> root) {
        if (root != null) {
            stack.push(root);
        }
    }
    
    @Override
    public boolean hasNext() {
        return !stack.isEmpty();
    }
    
    @Override
    public T next() {
        if (!hasNext()) throw new NoSuchElementException();
        TreeNode<T> node = stack.pop();
        // 先压右子节点,再压左子节点,保证左子节点先被处理
        if (node.right != null) stack.push(node.right);
        if (node.left != null) stack.push(node.left);
        return node.value;
    }
}

/**
 * 中序遍历迭代器:左 -> 根 -> 右
 */
class InOrderIterator<T> implements Iterator<T> {
    private Stack<TreeNode<T>> stack = new Stack<>();
    private TreeNode<T> current;
    
    public InOrderIterator(TreeNode<T> root) {
        this.current = root;
    }
    
    @Override
    public boolean hasNext() {
        return current != null || !stack.isEmpty();
    }
    
    @Override
    public T next() {
        while (current != null) {
            stack.push(current);
            current = current.left;
        }
        if (!hasNext()) throw new NoSuchElementException();
        TreeNode<T> node = stack.pop();
        current = node.right;
        return node.value;
    }
}

/**
 * 后序遍历迭代器:左 -> 右 -> 根
 */
class PostOrderIterator<T> implements Iterator<T> {
    private Stack<TreeNode<T>> stack = new Stack<>();
    private TreeNode<T> lastVisited = null;
    
    public PostOrderIterator(TreeNode<T> root) {
        if (root != null) {
            pushLeftBranch(root);
        }
    }
    
    private void pushLeftBranch(TreeNode<T> node) {
        while (node != null) {
            stack.push(node);
            node = node.left;
        }
    }
    
    @Override
    public boolean hasNext() {
        return !stack.isEmpty();
    }
    
    @Override
    public T next() {
        if (!hasNext()) throw new NoSuchElementException();
        TreeNode<T> peek = stack.peek();
        
        // 如果右子树存在且未被访问过,转向右子树
        if (peek.right != null && peek.right != lastVisited) {
            pushLeftBranch(peek.right);
            return next();
        }
        
        // 否则访问当前节点
        lastVisited = stack.pop();
        return lastVisited.value;
    }
}

/**
 * 层序遍历迭代器:广度优先
 */
class LevelOrderIterator<T> implements Iterator<T> {
    private Queue<TreeNode<T>> queue = new LinkedList<>();
    
    public LevelOrderIterator(TreeNode<T> root) {
        if (root != null) {
            queue.offer(root);
        }
    }
    
    @Override
    public boolean hasNext() {
        return !queue.isEmpty();
    }
    
    @Override
    public T next() {
        if (!hasNext()) throw new NoSuchElementException();
        TreeNode<T> node = queue.poll();
        if (node.left != null) queue.offer(node.left);
        if (node.right != null) queue.offer(node.right);
        return node.value;
    }
}

// 客户端测试
public class TreeTraversalDemo {
    public static void main(String[] args) {
        TreeNode<Integer> root = new TreeNode<>(1);
        root.left = new TreeNode<>(2);
        root.right = new TreeNode<>(3);
        root.left.left = new TreeNode<>(4);
        root.left.right = new TreeNode<>(5);
        root.right.left = new TreeNode<>(6);
        root.right.right = new TreeNode<>(7);
        
        BinaryTree<Integer> tree = new BinaryTree<>(root);
        
        System.out.print("前序遍历: ");
        Iterator<Integer> preIter = tree.preOrderIterator();
        while (preIter.hasNext()) System.out.print(preIter.next() + " ");
        
        System.out.print("\n中序遍历: ");
        Iterator<Integer> inIter = tree.inOrderIterator();
        while (inIter.hasNext()) System.out.print(inIter.next() + " ");
        
        System.out.print("\n后序遍历: ");
        Iterator<Integer> postIter = tree.postOrderIterator();
        while (postIter.hasNext()) System.out.print(postIter.next() + " ");
        
        System.out.print("\n层序遍历: ");
        Iterator<Integer> levelIter = tree.levelOrderIterator();
        while (levelIter.hasNext()) System.out.print(levelIter.next() + " ");
    }
}

类图

classDiagram
    class TreeNode~T~ {
        +T value
        +TreeNode~T~ left
        +TreeNode~T~ right
    }

    class BinaryTree~T~ {
        -TreeNode~T~ root
        +preOrderIterator() Iterator~T~
        +inOrderIterator() Iterator~T~
        +postOrderIterator() Iterator~T~
        +levelOrderIterator() Iterator~T~
    }

    class Iterator~T~ {
        <<interface>>
        +hasNext() boolean
        +next() T
    }

    class PreOrderIterator~T~ {
        -Stack~TreeNode~T~~ stack
        +hasNext() boolean
        +next() T
    }

    class InOrderIterator~T~ {
        -Stack~TreeNode~T~~ stack
        -TreeNode~T~ current
        +hasNext() boolean
        +next() T
    }

    class PostOrderIterator~T~ {
        -Stack~TreeNode~T~~ stack
        -TreeNode~T~ lastVisited
        +hasNext() boolean
        +next() T
    }

    class LevelOrderIterator~T~ {
        -Queue~TreeNode~T~~ queue
        +hasNext() boolean
        +next() T
    }

    Iterator <|.. PreOrderIterator
    Iterator <|.. InOrderIterator
    Iterator <|.. PostOrderIterator
    Iterator <|.. LevelOrderIterator
    BinaryTree --> TreeNode
    PreOrderIterator --> TreeNode
    InOrderIterator --> TreeNode
    PostOrderIterator --> TreeNode
    LevelOrderIterator --> TreeNode

文字说明: 上图展示了二叉树聚合类与四种遍历迭代器之间的继承与依赖关系。BinaryTree作为聚合类,持有根节点root,并提供四个工厂方法分别创建不同遍历策略的迭代器。所有具体迭代器(PreOrderIteratorInOrderIteratorPostOrderIteratorLevelOrderIterator)均实现Iterator接口,但它们各自维护了不同的内部状态结构:前序、中序、后序迭代器使用Stack来模拟递归调用的系统栈,而层序迭代器使用Queue实现广度优先遍历。这种设计使得树结构的遍历算法与树结构本身完全解耦:树节点只关心数据存储和父子关系,而如何以特定顺序访问这些节点则完全由迭代器负责。客户端只需获取相应迭代器,调用统一的hasNext()next()即可完成遍历,无需理解栈或队列的复杂操作,也无需递归编程。这正是迭代器模式在复杂数据结构中的核心价值——将遍历算法模块化、可复用化

场景二:文件系统递归遍历

Demo:文件目录迭代器(支持深度优先/广度优先、文件类型过滤)

import java.io.File;
import java.io.FileFilter;
import java.util.*;

/**
 * 文件系统迭代器,支持DFS/BFS和文件类型过滤
 */
class FileSystemIterator implements Iterator<File> {
    private final Deque<File> stackOrQueue;  // 既可作为栈也可作为队列
    private final boolean depthFirst;        // true=深度优先(栈), false=广度优先(队列)
    private final FileFilter filter;
    private File nextFile;
    private boolean hasNext = false;
    
    public FileSystemIterator(File root, boolean depthFirst, FileFilter filter) {
        this.depthFirst = depthFirst;
        this.filter = filter;
        this.stackOrQueue = depthFirst ? new ArrayDeque<>() : new ArrayDeque<>();
        if (root != null && root.exists()) {
            stackOrQueue.add(root);
            advanceToNext();
        }
    }
    
    /**
     * 推进到下一个符合条件的文件(惰性加载)
     */
    private void advanceToNext() {
        hasNext = false;
        nextFile = null;
        
        while (!stackOrQueue.isEmpty()) {
            File current = stackOrQueue.pollFirst(); // 从头部取出
            
            if (current.isDirectory()) {
                File[] children = current.listFiles();
                if (children != null) {
                    if (depthFirst) {
                        // 深度优先:逆序压入栈,保证按字母顺序遍历
                        Arrays.sort(children, Comparator.comparing(File::getName).reversed());
                        for (File child : children) {
                            stackOrQueue.addFirst(child);
                        }
                    } else {
                        // 广度优先:顺序加入队尾
                        Arrays.sort(children, Comparator.comparing(File::getName));
                        for (File child : children) {
                            stackOrQueue.addLast(child);
                        }
                    }
                }
            }
            
            // 检查当前文件是否符合过滤条件
            if (filter.accept(current) && !current.isDirectory()) {
                nextFile = current;
                hasNext = true;
                return;
            }
        }
    }
    
    @Override
    public boolean hasNext() {
        return hasNext;
    }
    
    @Override
    public File next() {
        if (!hasNext) {
            throw new NoSuchElementException();
        }
        File result = nextFile;
        advanceToNext();  // 预取下一个
        return result;
    }
}

// 客户端使用示例
public class FileSystemIteratorDemo {
    public static void main(String[] args) {
        File root = new File("/path/to/your/project");
        
        // 深度优先遍历所有.java文件
        System.out.println("=== 深度优先遍历 .java 文件 ===");
        FileFilter javaFilter = file -> file.getName().endsWith(".java");
        Iterator<File> dfsIter = new FileSystemIterator(root, true, javaFilter);
        while (dfsIter.hasNext()) {
            System.out.println(dfsIter.next().getAbsolutePath());
        }
        
        // 广度优先遍历所有.xml文件
        System.out.println("\n=== 广度优先遍历 .xml 文件 ===");
        FileFilter xmlFilter = file -> file.getName().endsWith(".xml");
        Iterator<File> bfsIter = new FileSystemIterator(root, false, xmlFilter);
        while (bfsIter.hasNext()) {
            System.out.println(bfsIter.next().getAbsolutePath());
        }
    }
}

深度优先遍历流程图

flowchart TD
    Start([开始]) --> Init[初始化栈,压入根目录]
    Init --> CheckStack{栈是否为空?}
    CheckStack -->|是| End([遍历结束])
    CheckStack -->|否| Pop[弹出栈顶元素 current]
    Pop --> IsDir{current 是目录?}
    IsDir -->|是| ListChildren[列出所有子文件/子目录]
    ListChildren --> ReversePush[逆序压入栈中]
    ReversePush --> CheckFilter
    IsDir -->|否| CheckFilter{是否符合过滤条件?}
    CheckFilter -->|是| ReturnFile[返回该文件,并继续]
    CheckFilter -->|否| CheckStack
    ReturnFile --> CheckStack

文字说明: 上述流程图描绘了文件系统迭代器在深度优先模式下的内部工作流程。迭代器在构造时接收根目录,并将其压入一个双端队列(此处用作栈)。hasNext()next()方法通过调用私有方法advanceToNext()实现惰性加载。在advanceToNext()中,迭代器持续从栈顶弹出元素:若遇到目录,则读取该目录下的所有子项,并按照逆序压回栈中,以保证遍历顺序符合用户预期(例如按文件名字母顺序遍历);若遇到文件且通过过滤器检验,则将其缓存为nextFile并设置hasNext = true,然后立即返回。这种设计的关键优势在于惰性求值:迭代器不会一次性遍历整个文件系统并将所有结果收集到内存中,而是仅维护当前遍历路径所需的最小状态(一个栈或队列)。这对于包含数百万文件的大型目录树尤其重要——它使得客户端可以流式地逐个处理文件,内存占用保持恒定,且可以在任意时刻中断遍历而不会浪费已投入的IO开销。

场景三:数据库结果集流式处理

Demo:模拟JDBC ResultSet的迭代器封装

import java.sql.*;
import java.util.NoSuchElementException;

/**
 * 模拟Row对象,代表结果集中的一行
 */
class Row {
    private final Object[] columns;
    
    public Row(Object[] columns) {
        this.columns = columns.clone();
    }
    
    public Object get(int index) { return columns[index - 1]; }
    public String getString(int index) { return (String) columns[index - 1]; }
    public int getInt(int index) { return (Integer) columns[index - 1]; }
    
    @Override
    public String toString() {
        return Arrays.toString(columns);
    }
}

/**
 * 将ResultSet封装为迭代器的适配器
 * 实现AutoCloseable以确保资源释放
 */
class ResultSetIterator implements Iterator<Row>, AutoCloseable {
    private final ResultSet rs;
    private final int columnCount;
    private Boolean hasNextCache = null;  // 缓存hasNext结果
    
    public ResultSetIterator(ResultSet rs) throws SQLException {
        this.rs = rs;
        this.columnCount = rs.getMetaData().getColumnCount();
    }
    
    @Override
    public boolean hasNext() {
        if (hasNextCache != null) {
            return hasNextCache;
        }
        try {
            hasNextCache = rs.next();
            return hasNextCache;
        } catch (SQLException e) {
            throw new RuntimeException("Failed to advance ResultSet", e);
        }
    }
    
    @Override
    public Row next() {
        try {
            if (hasNextCache == null) {
                if (!rs.next()) {
                    throw new NoSuchElementException();
                }
            }
            hasNextCache = null;  // 消费掉缓存
            
            Object[] rowData = new Object[columnCount];
            for (int i = 1; i <= columnCount; i++) {
                rowData[i - 1] = rs.getObject(i);
            }
            return new Row(rowData);
        } catch (SQLException e) {
            throw new RuntimeException("Failed to retrieve row data", e);
        }
    }
    
    @Override
    public void close() throws Exception {
        Statement stmt = rs.getStatement();
        Connection conn = stmt != null ? stmt.getConnection() : null;
        rs.close();
        if (stmt != null) stmt.close();
        if (conn != null) conn.close();
    }
}

/**
 * 流式查询服务
 */
class StreamingQueryService {
    private final DataSource dataSource;
    
    public StreamingQueryService(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    /**
     * 执行流式查询,返回可迭代的结果集迭代器
     */
    public ResultSetIterator streamQuery(String sql) throws SQLException {
        Connection conn = dataSource.getConnection();
        // 关键:设置fetchSize和关闭自动提交以启用MySQL流式查询
        conn.setAutoCommit(false);
        Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, 
                                              ResultSet.CONCUR_READ_ONLY);
        stmt.setFetchSize(Integer.MIN_VALUE);  // MySQL流式读取标志
        ResultSet rs = stmt.executeQuery(sql);
        return new ResultSetIterator(rs);
    }
}

// 内存对比演示
public class StreamingVsBatchDemo {
    public static void main(String[] args) throws Exception {
        // 模拟大数据量查询
        DataSource ds = createMockDataSource();
        StreamingQueryService service = new StreamingQueryService(ds);
        
        // 方式一:流式迭代(内存友好)
        System.out.println("=== 流式迭代处理 ===");
        long startMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        try (ResultSetIterator iter = service.streamQuery("SELECT * FROM large_table")) {
            int count = 0;
            while (iter.hasNext()) {
                Row row = iter.next();
                // 处理每一行...
                count++;
                if (count % 100000 == 0) {
                    System.out.println("Processed " + count + " rows");
                }
            }
        }
        long endMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        System.out.println("Memory used: " + (endMemory - startMemory) / 1024 / 1024 + " MB");
        
        // 方式二:一次性加载到List(内存可能溢出)
        // List<Row> allRows = jdbcTemplate.query(sql, rowMapper); 
        // 当结果集过大时,此处将抛出OutOfMemoryError
    }
}

时序图

sequenceDiagram
    participant Client as 客户端
    participant Iterator as ResultSetIterator
    participant RS as ResultSet
    participant DB as 数据库
    
    Client->>Iterator: hasNext()
    Iterator->>RS: next()
    RS->>DB: 获取下一行数据
    DB-->>RS: 行数据(游标移动)
    RS-->>Iterator: true/false
    Iterator-->>Client: true/false
    
    alt 有下一行
        Client->>Iterator: next()
        Iterator->>RS: getObject(1..N)
        RS-->>Iterator: 列数据
        Iterator->>Iterator: 封装为Row对象
        Iterator-->>Client: Row对象
    end
    
    loop 循环直到hasNext()为false
        Client->>Iterator: hasNext()
        Iterator->>RS: next()
        RS-->>Iterator: false (无更多行)
        Iterator-->>Client: false
    end
    
    Client->>Iterator: close()
    Iterator->>RS: close()
    Iterator->>DB: 释放连接资源

文字说明: 上图时序图展示了流式处理数据库结果集时,客户端、迭代器适配器、底层ResultSet及数据库之间的交互时序。关键点在于迭代器的hasNext()方法实际上调用了ResultSet.next(),这不仅检查是否还有数据,更重要的是它驱动了数据库游标的移动。在流式模式下(通过setFetchSize(Integer.MIN_VALUE)启用),数据库并不会一次性将所有查询结果发送给客户端,而是每次仅发送一行(或一小批)数据。当客户端调用next()时,迭代器从ResultSet中逐列读取数据并封装成Row对象返回。这种设计的核心价值在于内存效率:无论结果集包含一百万行还是一亿行,客户端应用的内存占用始终维持在极低水平(仅当前处理的一行数据)。与之对比,传统的一次性加载方式会将所有行转换为Java对象并存入List,这在处理大结果集时极易导致OutOfMemoryError。流式迭代器的代价是数据库连接在整个遍历期间必须保持打开状态,因此必须配合try-with-resources确保资源最终被释放。

场景四:分页API统一迭代封装

Demo:PageIterator封装第三方分页API

import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;

/**
 * 通用的分页API迭代器
 * 支持预取(prefetch)优化
 */
class PageIterator<T> implements Iterator<T> {
    private final Function<Integer, List<T>> pageFetcher;  // 分页拉取函数
    private final int pageSize;
    private final int prefetchThreshold;  // 预取阈值(剩余元素少于此值时触发预取)
    
    private int currentPage = 0;
    private Queue<T> buffer = new ConcurrentLinkedQueue<>();
    private volatile boolean exhausted = false;
    private volatile boolean fetching = false;
    private final ExecutorService prefetchExecutor = Executors.newSingleThreadExecutor();
    
    public PageIterator(Function<Integer, List<T>> pageFetcher, int pageSize) {
        this(pageFetcher, pageSize, pageSize / 2);
    }
    
    public PageIterator(Function<Integer, List<T>> pageFetcher, int pageSize, int prefetchThreshold) {
        this.pageFetcher = pageFetcher;
        this.pageSize = pageSize;
        this.prefetchThreshold = prefetchThreshold;
    }
    
    @Override
    public boolean hasNext() {
        ensureBuffer();
        return !buffer.isEmpty();
    }
    
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        T item = buffer.poll();
        // 检查是否需要触发预取
        if (buffer.size() <= prefetchThreshold && !exhausted && !fetching) {
            prefetchNextPage();
        }
        return item;
    }
    
    private void ensureBuffer() {
        if (!buffer.isEmpty() || exhausted) {
            return;
        }
        fetchNextPageSync();
    }
    
    private synchronized void fetchNextPageSync() {
        if (!buffer.isEmpty() || exhausted) {
            return;
        }
        if (fetching) {
            // 等待异步预取完成
            while (fetching && buffer.isEmpty() && !exhausted) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            return;
        }
        // 同步拉取
        List<T> page = pageFetcher.apply(currentPage++);
        if (page == null || page.isEmpty()) {
            exhausted = true;
        } else {
            buffer.addAll(page);
            if (page.size() < pageSize) {
                exhausted = true;
            }
        }
    }
    
    private void prefetchNextPage() {
        if (fetching || exhausted) return;
        fetching = true;
        prefetchExecutor.submit(() -> {
            try {
                List<T> page = pageFetcher.apply(currentPage);
                if (page != null && !page.isEmpty()) {
                    buffer.addAll(page);
                    currentPage++;
                    if (page.size() < pageSize) {
                        exhausted = true;
                    }
                } else {
                    exhausted = true;
                }
            } finally {
                fetching = false;
            }
        });
    }
    
    public void shutdown() {
        prefetchExecutor.shutdown();
    }
}

// 模拟第三方分页API
class ThirdPartyApiClient {
    public List<String> fetchUsers(int page, int size) {
        // 模拟网络延迟
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        
        // 模拟分页数据
        int start = page * size;
        if (start >= 100) {  // 总共100条数据
            return Collections.emptyList();
        }
        List<String> users = new ArrayList<>();
        for (int i = 0; i < size && start + i < 100; i++) {
            users.add("User_" + (start + i));
        }
        return users;
    }
}

// 客户端使用
public class PageIteratorDemo {
    public static void main(String[] args) {
        ThirdPartyApiClient client = new ThirdPartyApiClient();
        
        PageIterator<String> iterator = new PageIterator<>(
            page -> client.fetchUsers(page, 20),
            20,
            5  // 当缓冲区剩余5个元素时触发预取
        );
        
        int count = 0;
        while (iterator.hasNext()) {
            String user = iterator.next();
            System.out.println(user);
            count++;
            
            // 模拟处理耗时
            try { Thread.sleep(50); } catch (InterruptedException e) {}
        }
        System.out.println("Total users: " + count);
        iterator.shutdown();
    }
}

分页迭代器流程图

flowchart TD
    Start(["next()调用"]) --> CheckBuffer{"缓冲区有元素?"}
    CheckBuffer -->|"有"| Poll["取出并返回元素"]
    Poll --> CheckThreshold{"缓冲区大小 <= 预取阈值?"}
    CheckThreshold -->|"是"| TriggerPrefetch["触发异步预取任务"]
    TriggerPrefetch --> End(["返回"])
    CheckThreshold -->|"否"| End
    
    CheckBuffer -->|"无"| CheckExhausted{"已耗尽?"}
    CheckExhausted -->|"是"| Throw["抛出NoSuchElementException"]
    Throw --> ErrorEnd(["异常结束"])
    CheckExhausted -->|"否"| Fetching{"正在拉取中?"}
    Fetching -->|"是"| Wait["等待异步拉取完成"]
    Wait --> CheckBuffer
    Fetching -->|"否"| SyncFetch["同步拉取下一页"]
    SyncFetch --> UpdateBuffer["更新缓冲区"]
    UpdateBuffer --> UpdateExhausted{"页大小 < pageSize?"}
    UpdateExhausted -->|"是"| SetExhausted["标记耗尽"]
    SetExhausted --> CheckBuffer
    UpdateExhausted -->|"否"| CheckBuffer

文字说明: 上述流程图揭示了分页迭代器内部的状态机逻辑,特别是预取优化的执行路径。当客户端调用next()时,迭代器首先检查本地缓冲区是否有元素。若有,则直接返回并随后判断缓冲区剩余元素数量是否已降至预设的预取阈值以下;若是,则通过独立线程异步发起下一页的拉取请求。这种预取(prefetch)机制能有效隐藏网络IO延迟:在客户端消费当前批次数据的同时,下一批数据已在后台准备就绪。当缓冲区为空且未耗尽时,若检测到已有异步拉取正在进行,next()会短暂自旋等待;否则将同步执行拉取以立即满足需求。exhausted标志位在返回的数据量小于pageSize时被置为true,标志着数据源已无更多数据。这种设计将复杂的分页逻辑、异步预取、线程协调全部封装在迭代器内部,对外暴露的仅仅是标准的Iterator接口,使得客户端可以像遍历内存集合一样遍历远程分页数据,大大简化了业务代码的复杂度。

场景五:多数据源联合迭代

Demo:UnionIterator实现多迭代器串联

import java.util.*;

/**
 * 联合迭代器:依次遍历多个迭代器中的元素
 */
class UnionIterator<T> implements Iterator<T> {
    private final Iterator<Iterator<T>> metaIterator;  // 迭代器的迭代器
    private Iterator<T> currentIterator;
    private boolean currentValid = false;
    private T nextElement;
    private boolean hasNext = false;
    
    @SafeVarargs
    public UnionIterator(Iterator<T>... iterators) {
        this(Arrays.asList(iterators).iterator());
    }
    
    public UnionIterator(Iterator<Iterator<T>> metaIterator) {
        this.metaIterator = metaIterator;
        advanceToNext();
    }
    
    /**
     * 推进到下一个有效元素
     */
    private void advanceToNext() {
        hasNext = false;
        nextElement = null;
        
        while (true) {
            // 如果当前迭代器有效且还有元素,直接取下一个
            if (currentValid && currentIterator.hasNext()) {
                nextElement = currentIterator.next();
                hasNext = true;
                return;
            }
            
            // 当前迭代器耗尽,尝试切换到下一个迭代器
            currentValid = false;
            if (metaIterator.hasNext()) {
                currentIterator = metaIterator.next();
                currentValid = true;
                // 跳过空的迭代器
                if (!currentIterator.hasNext()) {
                    continue;
                }
                // 继续循环,将在下一轮返回元素
            } else {
                // 没有更多迭代器了
                return;
            }
        }
    }
    
    @Override
    public boolean hasNext() {
        return hasNext;
    }
    
    @Override
    public T next() {
        if (!hasNext) {
            throw new NoSuchElementException();
        }
        T result = nextElement;
        advanceToNext();  // 预取下一个
        return result;
    }
}

// 支持动态添加迭代器的增强版
class DynamicUnionIterator<T> implements Iterator<T> {
    private final Queue<Iterator<T>> iteratorQueue = new LinkedList<>();
    private Iterator<T> currentIterator;
    private T nextElement;
    private boolean hasNext = false;
    
    public void addIterator(Iterator<T> iterator) {
        if (iterator != null && iterator.hasNext()) {
            iteratorQueue.offer(iterator);
        }
    }
    
    @Override
    public boolean hasNext() {
        if (hasNext) return true;
        advanceToNext();
        return hasNext;
    }
    
    @Override
    public T next() {
        if (!hasNext()) throw new NoSuchElementException();
        T result = nextElement;
        hasNext = false;
        nextElement = null;
        return result;
    }
    
    private void advanceToNext() {
        // 尝试从当前迭代器获取
        if (currentIterator != null && currentIterator.hasNext()) {
            nextElement = currentIterator.next();
            hasNext = true;
            return;
        }
        
        // 切换到下一个迭代器
        currentIterator = iteratorQueue.poll();
        while (currentIterator != null) {
            if (currentIterator.hasNext()) {
                nextElement = currentIterator.next();
                hasNext = true;
                return;
            }
            currentIterator = iteratorQueue.poll();
        }
        // 所有迭代器均耗尽
        hasNext = false;
    }
}

// 使用示例
public class UnionIteratorDemo {
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("A", "B", "C");
        List<String> list2 = Arrays.asList("D", "E");
        List<String> list3 = Arrays.asList("F", "G", "H", "I");
        
        UnionIterator<String> union = new UnionIterator<>(
            list1.iterator(),
            list2.iterator(),
            list3.iterator()
        );
        
        System.out.print("联合迭代结果: ");
        while (union.hasNext()) {
            System.out.print(union.next() + " ");
        }
        // 输出: A B C D E F G H I
    }
}

联合迭代器状态机流程图

flowchart TD
    Start([advanceToNext]) --> CheckCurrent{currentIterator<br/>有效且有元素?}
    CheckCurrent -->|是| Fetch[从currentIterator取next]
    Fetch --> SetResult[设置nextElement<br/>hasNext=true]
    SetResult --> End([返回])
    
    CheckCurrent -->|否| HasMoreMeta{metaIterator<br/>还有迭代器?}
    HasMoreMeta -->|是| GetNextIter[获取下一个Iterator]
    GetNextIter --> SetAsCurrent[设为currentIterator<br/>currentValid=true]
    SetAsCurrent --> CheckCurrentEmpty{currentIterator<br/>为空?}
    CheckCurrentEmpty -->|是| HasMoreMeta
    CheckCurrentEmpty -->|否| CheckCurrent
    
    HasMoreMeta -->|否| NoMore[hasNext=false]
    NoMore --> End

文字说明: 联合迭代器的核心在于它维护了一个“迭代器的迭代器”——metaIterator。上图状态机展示了advanceToNext()方法的决策逻辑:首先检查当前的子迭代器currentIterator是否有效且包含更多元素,若是则直接从中取出下一个元素并标记hasNext=true。若当前子迭代器已耗尽(hasNext()返回false),则转向metaIterator请求下一个子迭代器,并将其设置为新的currentIterator。这里有一个重要的优化:在设置新迭代器后,立即检查它是否为空(即第一个hasNext()即为false),若是则跳过该迭代器继续向后寻找。这种设计优雅地处理了空迭代器边界情况,确保了联合迭代器的hasNext()next()行为的正确性。联合迭代器模式在现实中有广泛应用,例如:合并来自多个数据库分片的结果集、聚合多个微服务返回的列表数据、或者将多个日志文件的读取流合并为一个统一的日志流。通过将多个数据源抽象为统一的迭代器接口,业务层代码无需关心数据来自何处、有多少个源,只需专注于如何处理每一个元素。


七、面试题精选与专家级解答

Q1: 为什么Java集合框架中Iterator设计为接口,而Iterable设计为另一个接口?二者如何配合实现增强for循环?

参考答案IteratorIterable的分工体现了单一职责原则接口隔离原则

  • Iterator接口:定义的是“迭代器”的行为契约,即如何遍历元素(hasNext()next()remove())。它代表的是遍历过程中的状态机
  • Iterable接口:定义的是“可迭代”的能力声明,即一个聚合对象能够产生遍历自身的迭代器(iterator()方法)。它代表的是聚合对象的一种能力特征

二者的配合机制:Java编译器在处理增强for循环(for (T item : collection))时,会检查collection是否实现了Iterable接口。如果是,编译器将其翻译为:

for (Iterator<T> it = collection.iterator(); it.hasNext(); ) {
    T item = it.next();
    // 循环体
}

如果聚合对象直接实现Iterator而不实现Iterable,则无法使用增强for循环,因为编译器无法获取迭代器的起始状态。此外,Iterable允许一个聚合对象被多次遍历(每次调用iterator()都返回一个新的迭代器实例),而如果聚合对象自身就是迭代器,则只能被遍历一次。

Q2: ArrayList的Iterator是如何实现fail-fast机制的?modCount和expectedModCount的作用是什么?

参考答案ArrayList的fail-fast机制是通过modCountexpectedModCount两个计数器实现的。

  • modCount:定义在AbstractList中,记录列表结构性修改(改变元素数量的操作,如addremoveclear)的次数。每次结构性修改都会使其递增。
  • expectedModCount:在迭代器(Itr)内部定义,创建迭代器时被初始化为modCount的当前值。

在迭代器的next()remove()方法中,首先调用checkForComodification()方法,检查modCount是否等于expectedModCount。若不等,说明在迭代器创建之后,列表被外部代码结构性修改了,此时抛出ConcurrentModificationException

特殊处理:迭代器自身的remove()方法在删除元素后,会执行expectedModCount = modCount,同步这两个计数器,因此迭代器自己的删除操作是合法的,不会触发异常。这种设计能够快速暴露并发修改的bug,遵循“快速失败”原则,但并不能保证绝对的并发安全。

Q3: CopyOnWriteArrayList的迭代器为什么是弱一致性的?它的实现与ArrayList迭代器有何本质区别?

参考答案CopyOnWriteArrayList的迭代器(COWIterator)具有弱一致性,因为它在创建时持有了底层数组的一个快照(snapshot)

  • 实现方式COWIterator的构造函数直接接收创建时的内部数组引用Object[] snapshot = elements,并在整个生命周期中只遍历这个快照数组,完全不关心原始列表后续的修改。
  • 与ArrayList迭代器的本质区别
    1. 数据源ArrayList.Itr通过外部类引用ArrayList.this.elementData访问列表的当前数组,因此能感知到并发修改;COWIterator持有的是独立的快照副本,与原始列表后续修改隔离。
    2. 修改检测ArrayList.ItrexpectedModCount检测机制;COWIterator没有该机制,也不抛出ConcurrentModificationException
    3. 写操作支持ArrayList.Itr支持remove()COWIterator不支持任何修改操作(remove/set/add均抛出UnsupportedOperationException)。
    4. 一致性语义ArrayList.Itr要求遍历期间不发生并发修改(强一致性前提);COWIterator允许遍历期间任意修改,但看到的数据是创建时的快照(弱一致性),可能不是最新数据。

弱一致性是CopyOnWriteArrayList“读写分离”和“写时复制”策略的自然结果,适合读多写少的并发场景。

Q4: Java 8中引入的Spliterator与Iterator有什么区别?它与并行流的关系是什么?

参考答案Spliterator(Splittable Iterator,可分割迭代器)是Java 8为支持并行遍历而引入的新接口,主要区别如下:

特性IteratorSpliterator
遍历方向仅支持顺序遍历支持顺序遍历和分割并行遍历
核心方法hasNext(), next()tryAdvance(), trySplit(), estimateSize()
并行能力无,自身不支持并行通过trySplit()将数据源分割为多个部分并行处理
特征描述通过characteristics()返回特征值(ORDERED、SIZED、SUBSIZED等)
使用方式外部显式循环通常由Stream API内部使用,用户较少直接调用

与并行流的关系: 当调用集合的parallelStream()时,Stream API会通过集合的spliterator()方法获取Spliterator。然后框架不断调用trySplit()将数据源递归分割成多个小片段,分配给ForkJoinPool中的不同线程并行处理。每个片段由一个Spliterator表示,线程调用tryAdvance()forEachRemaining()处理其中的元素。Spliteratorcharacteristics()提供的元数据(如是否有序、大小是否已知)有助于Stream框架选择最优的并行策略。

Q5: 如何实现一个支持在迭代过程中安全删除元素的迭代器?请说明JDK Iterator.remove()的设计考量。

参考答案: 要实现安全的迭代中删除,关键在于由迭代器自身来执行删除操作,而不是直接调用聚合对象的删除方法。

设计考量

  1. 位置管理:迭代器在调用remove()时必须知道自己刚刚返回了哪个元素。因此迭代器内部维护lastRet(最后一次返回元素的索引)。
  2. 状态校验:调用remove()前必须确保已经调用过next(),且没有连续两次调用remove()。这通过检查lastRet是否为有效索引(>=0)来实现。
  3. 并发检测同步:执行删除后,迭代器必须更新自己的expectedModCount以匹配聚合对象更新后的modCount,确保后续操作不会误报并发修改异常。
  4. 游标调整:删除元素后,迭代器的游标cursor必须相应调整(例如,在ArrayList中删除元素后,后续元素前移,cursor应减1)。

JDK Iterator.remove()设计为“可选操作”的原因:并非所有迭代器都需要支持删除(如只读集合的迭代器),因此remove()被设计为默认抛出UnsupportedOperationException,由子类选择性覆盖。这遵循了接口隔离原则,避免强迫不需要删除功能的实现类提供空实现。

Q6: 迭代器模式与Stream API中的内部迭代有何区别?各自适用于什么场景?

参考答案

对比维度迭代器模式(外部迭代)Stream API(内部迭代)
控制权客户端显式控制迭代过程(何时取下一个)库内部控制迭代过程,客户端只提供行为
代码风格命令式(Imperative)声明式(Declarative)
中断能力可随时break退出需通过limit()takeWhile()等操作限制
状态管理迭代器状态暴露给客户端状态封装在Stream管道内部
并行支持需客户端自行处理多线程内置parallel()支持,透明切换
操作组合操作逻辑写在循环体内可通过mapfilterreduce等组合

适用场景

  • 使用外部迭代器:需要精细控制遍历过程(如根据复杂条件提前终止、需要访问迭代器状态)、需要在遍历中修改集合本身、或者需要跨多个集合进行联合遍历时。
  • 使用内部迭代(Stream):处理批量数据的过滤、映射、聚合等流水线操作、希望利用并行计算提升性能、偏好声明式编程风格时。

两者并非互斥,Stream API底层正是基于Spliterator(一种增强的迭代器)构建的,可以说是迭代器模式在函数式编程范式下的进化形态。

Q7: 在分布式分页查询场景中,如何用迭代器模式封装游标分页,避免深分页的性能问题?

参考答案: 深分页(LIMIT 10000, 20)在分布式数据库中性能极差,因为数据库需要扫描并丢弃前10000条数据。游标分页(Cursor Pagination)通过记录上一次查询的最后一条记录的排序字段值作为游标,每次查询WHERE sort_key > last_cursor LIMIT 20,避免了大量跳过。

迭代器封装设计

  1. 状态维护:迭代器内部维护lastCursor(上一次返回的最后元素的游标值)和currentBatch缓存。
  2. 惰性加载hasNext()在本地缓存为空时,使用当前游标向数据源发起查询。
  3. 游标更新:处理完一批数据后,提取最后一条记录的排序字段作为新的lastCursor
  4. 终止条件:当返回的记录数小于pageSize时,标记exhausted = true

关键代码骨架

public class CursorPageIterator<T> implements Iterator<T> {
    private String nextCursor;
    private List<T> buffer;
    
    @Override
    public boolean hasNext() {
        if (bufferNotEmpty()) return true;
        if (exhausted) return false;
        Page<T> page = fetchPage(nextCursor);
        buffer = page.getItems();
        nextCursor = page.getNextCursor();
        exhausted = page.isLast();
        return !buffer.isEmpty();
    }
}

这种封装使客户端无需关心底层是offset/limit还是cursor分页,也无需手动管理游标,实现了遍历逻辑与分页策略的解耦。

Q8: MyBatis的Cursor接口是如何实现流式迭代的?它与直接返回List有何优劣?

参考答案: MyBatis的Cursor接口通过以下机制实现流式迭代:

  1. 底层依赖JDBC的流式ResultSet:设置fetchSizeInteger.MIN_VALUE(对于MySQL)或正值(对于兼容JDBC标准的数据库),使得数据库驱动逐行发送数据。
  2. 惰性加载Cursor内部的迭代器在hasNext()时才调用ResultSet.next()获取下一行。
  3. 资源管理Cursor继承AutoCloseable,确保底层ResultSetStatementConnection能及时释放。

对比List返回方式

方面Cursor流式迭代返回List
内存占用极低(仅当前行)高(全部结果集)
响应首行速度快,可尽早处理第一行慢,必须等待全部加载
数据库连接遍历期间一直占用结果集读取完毕即可释放
编程复杂度需使用try-with-resources简单
适用场景大结果集(百万级)、报表导出小结果集、需随机访问

注意:使用Cursor时必须确保在事务或会话关闭前完成遍历,否则连接被回收会导致遍历中断。

Q9: 如何设计一个线程安全的迭代器?需要考虑哪些并发问题?

参考答案: 设计线程安全的迭代器面临两种不同的设计哲学,需根据场景选择:

方案一:强一致性快照迭代器(类似CopyOnWriteArrayList

  • 设计:迭代器创建时复制整个底层数据结构(或获取不可变快照)。
  • 并发问题:无,迭代器独立于原始数据。
  • 代价:内存开销大,数据可能过时。

方案二:支持有限并发修改的迭代器(类似ConcurrentHashMap

  • 设计:迭代器直接遍历底层数据结构的当前状态,不抛出ConcurrentModificationException
  • 并发问题
    • 弱一致性:可能看到部分更新,甚至同一个元素出现两次或遗漏。
    • 结构性修改安全性:必须确保迭代期间即使发生rehash(如HashMap扩容)也不会导致死循环或NullPointerException
  • 代价:实现复杂,语义较弱。

方案三:互斥锁保护(不推荐)

  • 设计:迭代期间对整个聚合对象加锁。
  • 并发问题:无并发修改问题,但性能极差,可能导致死锁。

推荐实践:对于大多数业务场景,应优先使用java.util.concurrent包提供的并发集合,它们的迭代器已经过精心设计。如需自定义,应明确一致性语义并文档化。

Q10: 迭代器模式和访问者模式都可以遍历对象结构,它们的本质区别是什么?在什么场景下选择哪种模式?

参考答案本质区别

  • 控制权反转:迭代器模式中,客户端控制遍历节奏——客户端决定何时调用next(),并在循环内对元素执行操作。访问者模式中,对象结构控制遍历过程——对象结构在accept方法中遍历自身元素,并对每个元素调用访问者的visit方法。
  • 操作与结构的耦合:迭代器模式将“遍历”与“操作”分离,操作逻辑写在客户端循环中,添加新操作无需修改聚合对象。访问者模式将“操作”封装在访问者中,对象结构通过双分派将自身传递给访问者;添加新操作只需新增访问者,但对象结构发生变化(如新增元素类型)时需要修改所有访问者。

场景选择指南

场景特征推荐模式原因
需要灵活控制遍历过程(暂停、跳过、中断)迭代器模式客户端掌握控制权
需要对同一组元素执行多种不同的、不相关的操作访问者模式新操作只需添加新访问者,符合开闭原则
对象结构中的元素类型相对固定,但操作经常变化访问者模式访问者能很好地集中管理相关操作
对象结构中的元素类型经常变化(新增子类)迭代器模式访问者模式需要修改所有访问者接口
遍历算法本身是核心复杂度所在(如树的各种遍历)迭代器模式可将不同遍历算法封装为不同迭代器
需要在遍历过程中积累跨元素的状态两者皆可迭代器在客户端维护状态;访问者在访问者对象内部维护状态

八、图表语法通用要求说明

本文所有Mermaid图表严格遵循用户要求:

  • 类图使用 classDiagram 语法,清晰标注抽象迭代器、具体迭代器、抽象聚合、具体聚合及客户端之间的关系。
  • 时序图使用 sequenceDiagram 语法,展示对象间的消息传递顺序。
  • 流程图使用 flowchart 语法(严禁使用已废弃的 graph 语法),描绘算法或状态转移逻辑。

每张图表下方均配有不少于200字的详细解读,剖析图表的构成要素、执行流程以及背后的设计意图,确保读者不仅看得懂图,更能理解图所承载的设计思想。


九、结语

迭代器模式看似简单,实则蕴含了深厚的设计智慧。它不仅是一个“遍历工具”,更是一种封装变化解耦行为与结构的经典范式。从JDK集合框架中IteratorIterable的精妙分工,到Spring、MyBatis中迭代器模式在资源遍历、流式查询中的深度应用,再到分布式系统中远程迭代器对海量数据分页遍历的优雅封装,迭代器模式的影响无处不在。

作为Java专家级开发者,深入理解迭代器模式不仅意味着能够熟练使用iterator()方法,更意味着能够洞察其背后的fail-fast机制、弱一致性语义、惰性求值策略,并能够在复杂的分布式场景中创造性地运用迭代器思想设计出高内聚、低耦合的系统组件。

希望本文能够帮助你建立起对迭代器模式的立体认知,在未来的系统设计与面试中游刃有余。遍历不止,精进不息。