在软件开发中,我们经常需要处理各种集合对象(如数组、列表、树等)的遍历问题。传统做法是直接在业务代码中暴露集合的内部结构,但这会导致几个严重问题:
迭代器模式通过将遍历行为抽象为独立对象,完美解决了这些问题。我在实际项目中见过太多因为不当遍历导致的代码维护噩梦,而合理使用迭代器模式可以让代码的可维护性提升一个数量级。
典型的迭代器模式包含以下核心角色:
code复制[集合接口] <<interface>>
+createIterator()
[具体集合] implements 集合接口
+createIterator()
[迭代器接口] <<interface>>
+hasNext()
+next()
[具体迭代器] implements 迭代器接口
-currentPosition
+hasNext()
+next()
java复制// 集合接口
interface Aggregate {
Iterator createIterator();
}
// 具体集合
class ConcreteAggregate implements Aggregate {
private String[] items = {"A", "B", "C"};
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
public int size() {
return items.length;
}
public String get(int index) {
return items[index];
}
}
// 迭代器接口
interface Iterator {
boolean hasNext();
Object next();
}
// 具体迭代器
class ConcreteIterator implements Iterator {
private ConcreteAggregate aggregate;
private int position = 0;
public ConcreteIterator(ConcreteAggregate aggregate) {
this.aggregate = aggregate;
}
@Override
public boolean hasNext() {
return position < aggregate.size();
}
@Override
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return aggregate.get(position++);
}
}
关键点:迭代器对象持有对集合的引用,但对外只暴露遍历接口。这种设计使得我们可以:
- 同时进行多个遍历
- 实现不同的遍历策略
- 不暴露集合内部结构
在处理组织结构、菜单系统等树形数据时,迭代器模式特别有用。我们可以实现:
每种迭代器实现相同的接口,客户端可以自由切换遍历方式而不需要修改业务代码。
JDBC中的ResultSet本质上就是一个迭代器实现。这种设计使得:
Java集合框架是迭代器模式的经典实现:
java复制List<String> list = Arrays.asList("a", "b", "c");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
这种设计使得所有集合类型提供统一的遍历接口,极大提升了API的易用性。
在多线程环境下,我们需要考虑迭代器的线程安全问题。常见解决方案:
对于大型数据集,可以实现惰性求值的迭代器:
java复制class LazyIterator implements Iterator {
private DataSource source;
private Object current;
public boolean hasNext() {
if (current == null) {
current = source.fetchNext();
}
return current != null;
}
public Object next() {
if (!hasNext()) throw new NoSuchElementException();
Object result = current;
current = null;
return result;
}
}
这种实现可以显著降低内存消耗。
当需要遍历多个集合时,可以创建组合迭代器:
java复制class CompositeIterator implements Iterator {
private Iterator[] iterators;
private int current = 0;
public boolean hasNext() {
while (current < iterators.length) {
if (iterators[current].hasNext()) {
return true;
}
current++;
}
return false;
}
public Object next() {
if (!hasNext()) throw new NoSuchElementException();
return iterators[current].next();
}
}
| 对比维度 | 迭代器模式 | 传统for循环 |
|---|---|---|
| 耦合度 | 低 | 高 |
| 扩展性 | 强 | 弱 |
| 线程安全 | 易实现 | 难保证 |
| 代码量 | 较多 | 较少 |
虽然两者都用于处理集合元素,但有本质区别:
在实际项目中,经常组合使用这两种模式。
在迭代过程中修改集合(导致未定义行为)
忽略资源释放(如数据库游标未关闭)
过度设计简单场景
随着语言发展,迭代器模式有了新的表现形式:
Java 8的Stream API就是典型的内部迭代器:
java复制list.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.forEach(System.out::println);
这种声明式风格更符合现代编程趋势。
Python、C#等语言通过yield关键字简化迭代器实现:
python复制def count_down(n):
while n > 0:
yield n
n -= 1
for i in count_down(5):
print(i) # 输出5,4,3,2,1
这种实现方式更加简洁直观。
对于频繁创建的迭代器对象,可以使用对象池减少GC压力:
java复制class IteratorPool {
private static final Map<Class<?>, Queue<Iterator>> pool = new HashMap<>();
public static <T> Iterator<T> borrow(Collection<T> collection) {
Queue<Iterator> queue = pool.computeIfAbsent(
collection.getClass(), k -> new LinkedList<>());
Iterator<T> it = queue.poll();
if (it == null) {
it = collection.iterator();
}
return it;
}
public static void release(Iterator<?> it) {
// 重置迭代器状态
Queue<Iterator> queue = pool.get(it.getClass());
if (queue != null) {
queue.offer(it);
}
}
}
对于大型集合,可以实现按需加载的迭代器:
java复制class LazyLoadingIterator implements Iterator {
private int batchSize = 100;
private int currentBatch = 0;
private List<Data> currentItems;
public boolean hasNext() {
if (currentItems == null || currentBatch * batchSize >= currentItems.size()) {
loadNextBatch();
}
return !currentItems.isEmpty();
}
private void loadNextBatch() {
currentItems = dao.fetchBatch(currentBatch++, batchSize);
}
}
java复制@Test
void testIterator() {
List<String> mockList = Arrays.asList("a", "b", "c");
Iterator<String> it = mockList.iterator();
assertTrue(it.hasNext());
assertEquals("a", it.next());
assertTrue(it.hasNext());
assertEquals("b", it.next());
assertTrue(it.hasNext());
assertEquals("c", it.next());
assertFalse(it.hasNext());
assertThrows(NoSuchElementException.class, () -> it.next());
}
迭代器模式常与其他模式配合使用:
在实际架构设计中,这些模式的组合可以产生强大的协同效应。