迭代器与适配器:Java设计模式的精妙实践

迭代器模式:统一集合遍历的利器

迭代器模式(Iterator Pattern)是Java集合框架中最经典的设计模式之一,它提供了一种统一的方式来遍历各种集合对象,而不需要暴露集合的内部表示。

Iterable与Iterator接口解析

Java通过IterableIterator两个核心接口实现迭代器模式:

public interface Iterable<T> {
    Iterator<T> iterator();
}

public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
}

工作原理

  1. Iterable表示"可迭代的",实现该接口的类可以通过iterator()方法获取迭代器
  2. 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());
}

迭代器模式的优势

  1. 解耦遍历与存储:客户端不需要知道集合的内部结构
  2. 支持多种遍历方式:可以为同一集合提供不同的迭代器
  3. 并行遍历:多个迭代器可以独立遍历同一集合

实践建议

  • 优先使用增强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
}

这个ArrayListArrays的静态内部类,它适配了数组到List接口:

图1

重要特性

  1. 固定大小:不能添加或删除元素(会抛出UnsupportedOperationException
  2. 数据共享:对列表的修改会反映到原始数组
  3. 非完全独立的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");

适配器模式的应用场景

  1. 接口转换:将旧系统接口适配到新系统
  2. 第三方库集成:统一不同库的接口
  3. 测试替身:创建测试专用的适配器

实践建议

  • 使用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());
}

性能考量与最佳实践

  1. 迭代器性能

    • ArrayList迭代比索引访问略慢(多一次方法调用)
    • LinkedList必须使用迭代器,索引访问性能极差(O(n))
  2. 适配器开销

    • Arrays.asList()几乎无开销,只是包装现有数组
    • 复杂适配器可能有转换计算开销
  3. 最佳实践

    • 对于只读操作,优先使用适配器而非拷贝
    • 频繁修改的数据应转换为适当集合类型
    • 考虑使用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集合框架的基石,理解它们的实现原理和适用场景,能够帮助我们:

  1. 编写更通用、更灵活的集合处理代码
  2. 更好地集成遗留代码和第三方库
  3. 设计出更清晰、更解耦的系统架构
  4. 在性能与抽象之间做出合理权衡

掌握这些模式不仅能够提升日常编码效率,更能培养出优秀的设计思维,为处理更复杂的系统设计问题打下坚实基础。

添加新评论