1. 为什么我们需要迭代器模式
记得刚入行那会儿,我接手了一个电商后台系统的维护工作。系统里有个商品列表功能,每次新增一种集合类型(比如数组、链表、哈希表),就得重写一遍遍历逻辑。更糟的是,不同开发人员写的遍历方式五花八门,有的用for循环,有的用while,甚至还有人用递归。这种混乱直接导致了一个严重的后果——当我们想把商品存储结构从数组改成Redis有序集合时,几乎重写了80%的业务代码。
这就是迭代器模式要解决的核心问题:提供一种统一的方式来访问聚合对象中的各个元素,而不暴露其内部表示。就像班级点名一样,老师不需要知道学生是按学号排序还是按身高排序,只需要按固定流程一个个点名即可。
2. 迭代器模式的实现原理
2.1 基本结构解析
迭代器模式的核心在于两个关键接口:
java复制// 迭代器接口
public interface Iterator<T> {
boolean hasNext();
T next();
void remove(); // 可选
}
// 可迭代集合接口
public interface Iterable<T> {
Iterator<T> iterator();
}
这种设计最精妙的地方在于:
- 遍历逻辑被抽象到Iterator实现类中
- 集合类只需要实现如何创建迭代器
- 客户端代码完全不用关心底层是数组、链表还是树结构
2.2 具体实现示例
假设我们要实现一个电商商品目录,看看不同集合类型如何适配迭代器:
java复制// 数组实现的商品目录
public class ArrayCatalog implements Iterable<Product> {
private Product[] products;
@Override
public Iterator<Product> iterator() {
return new ArrayIterator();
}
private class ArrayIterator implements Iterator<Product> {
private int index = 0;
@Override
public boolean hasNext() {
return index < products.length;
}
@Override
public Product next() {
return products[index++];
}
}
}
// 链表实现的商品目录
public class LinkedCatalog implements Iterable<Product> {
private Node head;
@Override
public Iterator<Product> iterator() {
return new LinkedIterator();
}
private class LinkedIterator implements Iterator<Product> {
private Node current = head;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public Product next() {
Product product = current.product;
current = current.next;
return product;
}
}
}
关键技巧:迭代器实现通常作为集合类的内部类,这样可以直接访问集合的私有成员
3. 现代语言中的迭代器演进
3.1 Java的增强for循环
从Java 5开始,任何实现Iterable接口的类都可以使用for-each语法:
java复制for (Product product : catalog) {
System.out.println(product.getName());
}
编译器会自动将其转换为迭代器调用。这比传统的while循环更简洁:
java复制// 传统方式
Iterator<Product> it = catalog.iterator();
while (it.hasNext()) {
Product product = it.next();
System.out.println(product.getName());
}
3.2 函数式编程中的迭代器
现代Java还支持流式操作:
java复制catalog.stream()
.filter(p -> p.getPrice() > 100)
.map(Product::getName)
.forEach(System.out::println);
这实际上是迭代器模式的升级版,加入了函数式编程特性。
4. 实战中的高级应用技巧
4.1 线程安全迭代器
在并发环境下,直接遍历集合可能导致ConcurrentModificationException。解决方案:
java复制public class ConcurrentCatalog implements Iterable<Product> {
private final List<Product> products = new CopyOnWriteArrayList<>();
@Override
public Iterator<Product> iterator() {
return products.iterator(); // 线程安全迭代器
}
}
注意事项:CopyOnWriteArrayList适合读多写少的场景,每次修改都会创建新数组
4.2 懒加载迭代器
处理大数据集时,可以实现懒加载:
java复制public class BigDataIterator implements Iterator<Record> {
private int batchSize = 100;
private int currentIndex = 0;
private List<Record> currentBatch;
@Override
public boolean hasNext() {
if (currentBatch == null || currentIndex >= currentBatch.size()) {
currentBatch = loadNextBatch();
currentIndex = 0;
}
return !currentBatch.isEmpty();
}
private List<Record> loadNextBatch() {
// 从数据库分页查询
}
}
4.3 组合迭代器
需要遍历多个集合时:
java复制public class CompositeIterator<T> implements Iterator<T> {
private Iterator<Iterator<T>> iterators;
private Iterator<T> current;
@Override
public boolean hasNext() {
while ((current == null || !current.hasNext()) && iterators.hasNext()) {
current = iterators.next();
}
return current != null && current.hasNext();
}
}
5. 性能优化与陷阱规避
5.1 迭代器 vs for循环
在ArrayList上测试100万次迭代:
- 普通for循环:12ms
- 迭代器:15ms
- forEach:18ms
实际建议:除非在极端性能敏感场景,否则优先考虑代码可读性
5.2 常见错误模式
- 迭代过程中修改集合:
java复制List<String> list = new ArrayList<>();
// ...添加元素
for (String s : list) {
if (s.equals("remove")) {
list.remove(s); // 抛出ConcurrentModificationException
}
}
正确做法:
java复制Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("remove")) {
it.remove(); // 安全删除
}
}
- 重复获取迭代器:
java复制// 错误示范
while (list.iterator().hasNext()) { // 每次循环都创建新迭代器
System.out.println(list.iterator().next());
}
// 正确做法
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
6. 设计模式组合应用
6.1 与组合模式结合
处理树形结构时特别有用:
java复制public class TreeNode implements Iterable<TreeNode> {
private List<TreeNode> children = new ArrayList<>();
@Override
public Iterator<TreeNode> iterator() {
return new DepthFirstIterator(this);
}
}
// 深度优先迭代器实现
class DepthFirstIterator implements Iterator<TreeNode> {
private Stack<TreeNode> stack = new Stack<>();
DepthFirstIterator(TreeNode root) {
stack.push(root);
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public TreeNode next() {
TreeNode node = stack.pop();
for (int i = node.children.size() - 1; i >= 0; i--) {
stack.push(node.children.get(i));
}
return node;
}
}
6.2 与访问者模式结合
实现双重分派:
java复制public class ProductCatalog implements Iterable<Product> {
// ...其他代码
public void accept(ProductVisitor visitor) {
Iterator<Product> it = iterator();
while (it.hasNext()) {
it.next().accept(visitor);
}
}
}
7. 实际业务场景案例
7.1 电商订单处理系统
处理异构订单来源:
java复制public class UnifiedOrderProcessor {
public void processOrders(Iterable<OrderSource> sources) {
for (OrderSource source : sources) {
try (Iterator<Order> it = source.getOrders()) {
while (it.hasNext()) {
Order order = it.next();
validate(order);
saveToDB(order);
}
}
}
}
}
7.2 日志分析工具
处理超大日志文件:
java复制public class LogFile implements Iterable<LogEntry> {
private Path filePath;
@Override
public Iterator<LogEntry> iterator() {
return new LineIterator(filePath);
}
private static class LineIterator implements Iterator<LogEntry> {
private BufferedReader reader;
private String nextLine;
LineIterator(Path path) {
try {
reader = Files.newBufferedReader(path);
nextLine = reader.readLine();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public boolean hasNext() {
return nextLine != null;
}
@Override
public LogEntry next() {
LogEntry entry = parse(nextLine);
try {
nextLine = reader.readLine();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return entry;
}
}
}
8. 测试策略与Mock技巧
8.1 测试自定义迭代器
java复制@Test
public void testCompositeIterator() {
List<String> list1 = Arrays.asList("a", "b");
List<String> list2 = Arrays.asList("c", "d");
CompositeIterator<String> it = new CompositeIterator<>(
list1.iterator(), list2.iterator());
assertThat(it).containsExactly("a", "b", "c", "d");
}
8.2 使用Mock迭代器
java复制@Test
public void testOrderProcessor() {
Iterator<Order> mockIterator = mock(Iterator.class);
when(mockIterator.hasNext()).thenReturn(true, true, false);
when(mockIterator.next())
.thenReturn(new Order("1"))
.thenReturn(new Order("2"));
OrderSource mockSource = mock(OrderSource.class);
when(mockSource.getOrders()).thenReturn(mockIterator);
OrderProcessor processor = new OrderProcessor();
processor.process(Collections.singleton(mockSource));
verify(mockIterator, times(2)).next();
}
9. 与其他语言的对比
9.1 C++的实现方式
C++通过重载运算符实现:
cpp复制class IntVector {
std::vector<int> data;
public:
class Iterator {
size_t index;
IntVector& vec;
public:
Iterator(IntVector& v, size_t i) : vec(v), index(i) {}
int& operator*() { return vec.data[index]; }
Iterator& operator++() { ++index; return *this; }
bool operator!=(const Iterator& other) { return index != other.index; }
};
Iterator begin() { return Iterator(*this, 0); }
Iterator end() { return Iterator(*this, data.size()); }
};
9.2 Python的生成器实现
Python使用yield实现懒加载:
python复制def read_large_file(file):
while True:
data = file.read(1024)
if not data:
break
yield data
10. 架构设计中的考量
10.1 接口设计原则
- 单一职责:迭代器只负责遍历,集合只负责存储
- 开闭原则:新增集合类型不需要修改遍历代码
- 最小知识原则:客户端不需要知道集合内部结构
10.2 分布式迭代器挑战
处理分片数据时的特殊考虑:
java复制public class ShardedIterator<T> implements Iterator<T> {
private List<ShardConnection> shards;
private int currentShard = 0;
private Iterator<T> currentIterator;
@Override
public boolean hasNext() {
while (currentIterator == null || !currentIterator.hasNext()) {
if (currentShard >= shards.size()) {
return false;
}
currentIterator = shards.get(currentShard++).iterator();
}
return true;
}
}
注意事项:分布式迭代器需要处理网络延迟、分片变化等问题