1. 迭代器模式的核心价值

十年前我刚入行时,第一次在Java集合框架中见到Iterator接口就产生了强烈好奇——为什么集合类不直接暴露内部结构,而要额外提供这个遍历工具?直到在电商系统里处理千万级订单数据时,我才真正理解迭代器模式的价值。

迭代器模式(Iterator Pattern)本质是 将集合对象的遍历行为抽象为独立迭代器对象 ,实现两大核心目标:

  1. 隔离遍历算法与数据结构(你不需要知道订单数据是用ArrayList还是Redis存储)
  2. 支持多种遍历方式(比如电商后台既需要正序浏览订单,又需要按金额倒序分析)

重要提示:在Java中直接使用for循环遍历集合,本质上仍是迭代器模式的语法糖。例如增强for循环会被编译器转换为Iterator实现。

2. 模式结构与实现原理

2.1 UML类图解析

// 典型迭代器模式实现
public interface Collection<T> {
    Iterator<T> createIterator();
}

public interface Iterator<T> {
    boolean hasNext();
    T next();
}

// 具体实现
public class OrderCollection implements Collection<Order> {
    private List<Order> orders = new ArrayList<>();
    
    @Override
    public Iterator<Order> createIterator() {
        return new OrderIterator(this);
    }
}

public class OrderIterator implements Iterator<Order> {
    private OrderCollection collection;
    private int index = 0;
    
    // 实现hasNext/next方法...
}

2.2 线程安全实现要点

在金融风控系统中,我们常需要线程安全的迭代器。以下是关键实现技巧:

  1. 快照迭代器 :在创建迭代器时拷贝集合数据(适合读多写少场景)
public Iterator<T> createIterator() {
    return new ArrayList<>(this.data).iterator(); 
}
  1. 失败快速(fail-fast)机制 :JDK的ArrayList迭代器采用此方案
private void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

3. 实战应用场景

3.1 电商订单分析系统

我们曾用迭代器模式处理这样的需求:

  • 基础遍历:按时间顺序浏览订单
  • 业务遍历:按订单金额降序(用于TOP商户分析)
  • 特殊遍历:仅筛选退货订单
// 业务迭代器实现示例
public class AmountDescIterator implements Iterator<Order> {
    private List<Order> orders;
    private int currentIndex;
    
    public AmountDescIterator(List<Order> orders) {
        this.orders = orders.stream()
            .sorted(Comparator.comparing(Order::getAmount).reversed())
            .collect(Collectors.toList());
    }
    
    // 实现迭代方法...
}

3.2 复合数据结构遍历

在文件系统处理中,我们设计过能递归遍历目录树的迭代器:

public class FileSystemIterator implements Iterator<File> {
    private Stack<Iterator<File>> stack = new Stack<>();
    
    public FileSystemIterator(File root) {
        stack.push(Collections.singletonList(root).iterator());
    }
    
    @Override
    public File next() {
        File current = stack.peek().next();
        if (current.isDirectory()) {
            stack.push(Arrays.asList(current.listFiles()).iterator());
        }
        return current;
    }
    
    // hasNext需要处理空目录等边界情况...
}

4. 性能优化实践

4.1 延迟加载优化

处理海量日志时,我们改进了迭代器实现:

public class LazyLogIterator implements Iterator<LogEntry> {
    private BufferedReader reader;
    private String nextLine;
    
    public LazyLogIterator(Path logPath) throws IOException {
        this.reader = Files.newBufferedReader(logPath);
        this.nextLine = reader.readLine(); // 预读第一行
    }
    
    @Override
    public boolean hasNext() {
        return nextLine != null;
    }
    
    @Override
    public LogEntry next() {
        LogEntry entry = parse(nextLine);
        nextLine = reader.readLine(); // 读取下一行
        return entry;
    }
}

4.2 内存映射文件迭代

对于超大型CSV文件(10GB+),我们采用内存映射方案:

public class MappedFileIterator implements Iterator<String> {
    private MappedByteBuffer buffer;
    private StringBuilder currentLine = new StringBuilder();
    
    // 实现细节涉及ByteBuffer的position操作...
}

5. 模式对比与选型

5.1 与访问者模式的区别

特性 迭代器模式 访问者模式
关注点 遍历机制 数据操作
数据结构耦合 松耦合(仅需实现迭代器) 需要接受访问者接口
典型场景 数据集合遍历 复杂数据结构处理

5.2 与Java Stream API的关系

Java 8的Stream API本质是迭代器模式的升级版:

  • 内置并行处理能力
  • 支持函数式操作链
  • 延迟执行特性

但迭代器模式在以下场景仍不可替代:

  1. 需要精细控制遍历过程(如条件中断)
  2. 处理非标准数据结构(如自定义树结构)
  3. 需要兼容老版本Java(Android开发常见)

6. 典型问题排查指南

6.1 ConcurrentModificationException

这是迭代器使用中最常见的运行时异常,解决方案包括:

  1. 改用并发集合类(如CopyOnWriteArrayList)
  2. 遍历前复制集合( new ArrayList<>(original)
  3. 使用迭代器的remove方法而非集合的remove

6.2 内存泄漏问题

在实现自定义迭代器时,特别注意:

// 错误示例:持有集合强引用
public class LeakIterator implements Iterator<T> {
    private Collection<T> collection; // 强引用!
    private int index;
    
    // ...
}

// 正确做法:弱引用或只保存必要数据
public class SafeIterator implements Iterator<T> {
    private WeakReference<Collection<T>> ref;
    // 或
    private T[] snapshotArray;
}

7. 现代语言中的演进

Kotlin通过 运算符重载 简化了迭代器实现:

class Matrix(val rows: Int, val cols: Int) {
    operator fun iterator(): Iterator<Pair<Int, Int>> {
        return object : Iterator<Pair<Int, Int>> {
            var row = 0
            var col = 0
            
            override fun hasNext() = row < rows && col < cols
            
            override fun next(): Pair<Int, Int> {
                val current = Pair(row, col)
                // 更新行列索引...
                return current
            }
        }
    }
}

// 使用时可直接for循环
for ((i,j) in matrix) {
    println("Element at $i,$j")
}

在JavaScript中,迭代器协议与生成器函数结合,创造了更优雅的实现:

function* fibonacci() {
    let [prev, curr] = [0, 1];
    while (true) {
        yield curr;
        [prev, curr] = [curr, prev + curr];
    }
}

// 使用
const sequence = fibonacci();
console.log(sequence.next().value); // 1

8. 设计启示与最佳实践

  1. 防御性拷贝原则 :当迭代器需要跨多线程使用时,应在创建时拷贝数据快照

  2. 空迭代器优化 :对于可能为空的数据集,返回特殊的空迭代器实例

public static <T> Iterator<T> emptyIterator() {
    return new Iterator<T>() {
        public boolean hasNext() { return false; }
        public T next() { throw new NoSuchElementException(); }
    };
}
  1. 组合迭代器模式 :与其他模式结合产生更大威力
  • 与工厂方法模式:创建不同类型的迭代器
  • 与组合模式:处理树形结构遍历
  • 与备忘录模式:实现迭代状态保存/恢复

在微服务架构中,我们常用迭代器模式实现:

  • 分页数据聚合(多个数据源的合并遍历)
  • 断路器模式下的降级数据遍历
  • 跨服务的数据流式处理

实现一个健壮的迭代器需要考虑的边界情况包括:

  • 源集合为null或空
  • 遍历过程中集合被修改
  • 多次调用next()而不检查hasNext()
  • 资源释放(如文件迭代器需要关闭流)
Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐