Java迭代器与适配器模式实践指南
迭代器与适配器:Java设计模式的精妙实践
迭代器模式:统一集合遍历的利器
迭代器模式(Iterator Pattern)是Java集合框架中最经典的设计模式之一,它提供了一种统一的方式来遍历各种集合对象,而不需要暴露集合的内部表示。
Iterable与Iterator接口解析
Java通过Iterable
和Iterator
两个核心接口实现迭代器模式:
public interface Iterable<T> {
Iterator<T> iterator();
}
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
工作原理:
Iterable
表示"可迭代的",实现该接口的类可以通过iterator()
方法获取迭代器Iterator
负责实际的遍历操作,包含hasNext()
和next()
等核心方法
自定义集合迭代器实现
让我们实现一个简单的书籍集合及其迭代器:
class Book {
private String title;
private String author;
// 构造方法、getter/setter省略
}
class BookShelf implements Iterable<Book> {
private Book[] books;
private int last = 0;
public BookShelf(int capacity) {
this.books = new Book[capacity];
}
public void addBook(Book book) {
books[last++] = book;
}
@Override
public Iterator<Book> iterator() {
return new BookIterator();
}
private class BookIterator implements Iterator<Book> {
private int index = 0;
@Override
public boolean hasNext() {
return index < last;
}
@Override
public Book next() {
return books[index++];
}
}
}
使用示例:
BookShelf shelf = new BookShelf(5);
shelf.addBook(new Book("Design Patterns", "GoF"));
shelf.addBook(new Book("Effective Java", "Bloch"));
for (Book book : shelf) { // 使用增强for循环
System.out.println(book.getTitle());
}
迭代器模式的优势
- 解耦遍历与存储:客户端不需要知道集合的内部结构
- 支持多种遍历方式:可以为同一集合提供不同的迭代器
- 并行遍历:多个迭代器可以独立遍历同一集合
实践建议:
- 优先使用增强for循环(
for-each
)而非显式迭代器,代码更简洁 - 实现
Iterable
而非直接返回Iterator
,以便支持增强for循环 - 注意迭代器的"快速失败"(fail-fast)特性,并发修改会抛出
ConcurrentModificationException
适配器模式:让不兼容的接口协同工作
适配器模式(Adapter Pattern)将一个类的接口转换成客户端期望的另一个接口,使原本不兼容的类可以一起工作。
数组与集合转换:Arrays.asList
Java中最常见的适配器实现之一是Arrays.asList()
,它将数组适配为List接口:
String[] strArray = {"Java", "Python", "C++"};
List<String> strList = Arrays.asList(strArray);
底层实现:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a); // 注意这是Arrays的内部类ArrayList
}
这个ArrayList
是Arrays
的静态内部类,它适配了数组到List
接口:
重要特性:
- 固定大小:不能添加或删除元素(会抛出
UnsupportedOperationException
) - 数据共享:对列表的修改会反映到原始数组
- 非完全独立的List实现:只实现了部分List方法
自定义适配器示例
假设我们有一个遗留的温度传感器,返回华氏度,我们需要适配为摄氏度:
// 遗留接口
class FahrenheitSensor {
public double getFahrenheit() {
return 98.6; // 示例值
}
}
// 目标接口
interface CelsiusSensor {
double getCelsius();
}
// 适配器
class SensorAdapter implements CelsiusSensor {
private FahrenheitSensor sensor;
public SensorAdapter(FahrenheitSensor sensor) {
this.sensor = sensor;
}
@Override
public double getCelsius() {
return (sensor.getFahrenheit() - 32) * 5 / 9;
}
}
使用方式:
FahrenheitSensor oldSensor = new FahrenheitSensor();
CelsiusSensor adaptedSensor = new SensorAdapter(oldSensor);
System.out.println("Temperature: " + adaptedSensor.getCelsius() + "°C");
适配器模式的应用场景
- 接口转换:将旧系统接口适配到新系统
- 第三方库集成:统一不同库的接口
- 测试替身:创建测试专用的适配器
实践建议:
- 使用
Arrays.asList()
时注意其固定大小的限制 - 需要可变List时,可以创建新ArrayList:
new ArrayList<>(Arrays.asList(array))
- 考虑使用Guava的
Lists.newArrayList()
或Java 8的Stream
进行更灵活的转换
模式对比与联合应用
迭代器与适配器的区别:
- 迭代器:关注遍历方式的统一
- 适配器:关注接口转换与兼容
联合应用示例:
// 将Enumeration适配为Iterator
public class EnumerationIterator<E> implements Iterator<E> {
private Enumeration<E> enumeration;
public EnumerationIterator(Enumeration<E> enumeration) {
this.enumeration = enumeration;
}
@Override
public boolean hasNext() {
return enumeration.hasMoreElements();
}
@Override
public E next() {
return enumeration.nextElement();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
// 使用示例
Vector<String> v = new Vector<>(Arrays.asList("A", "B", "C"));
Iterator<String> i = new EnumerationIterator<>(v.elements());
while (i.hasNext()) {
System.out.println(i.next());
}
性能考量与最佳实践
迭代器性能:
ArrayList
迭代比索引访问略慢(多一次方法调用)LinkedList
必须使用迭代器,索引访问性能极差(O(n))
适配器开销:
Arrays.asList()
几乎无开销,只是包装现有数组- 复杂适配器可能有转换计算开销
最佳实践:
- 对于只读操作,优先使用适配器而非拷贝
- 频繁修改的数据应转换为适当集合类型
- 考虑使用Java 8 Stream API进行更声明式的处理
// 使用Stream进行灵活转换
String[] array = {"Java", "Python", "C++"};
List<String> list = Arrays.stream(array)
.filter(s -> s.startsWith("J"))
.collect(Collectors.toList());
总结
迭代器和适配器模式是Java集合框架的基石,理解它们的实现原理和适用场景,能够帮助我们:
- 编写更通用、更灵活的集合处理代码
- 更好地集成遗留代码和第三方库
- 设计出更清晰、更解耦的系统架构
- 在性能与抽象之间做出合理权衡
掌握这些模式不仅能够提升日常编码效率,更能培养出优秀的设计思维,为处理更复杂的系统设计问题打下坚实基础。