迭代器模式

本文介绍迭代器模式的概念、设计类图及其在Java API中的应用。通过示例代码展示了如何使用迭代器模式遍历聚合对象中的元素,同时强调了迭代器模式在职责划分上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义:

提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。

设计类图:
设计类图
迭代器模式中涉及的角色:

  • 抽象迭代器(Iterator)角色:负责定义遍历和访问元素的接口,是所有迭代器必须实现的接口。
  • 具体迭代器(ConcreteIterator)角色:实现迭代器接口,记录目前遍历的位置,完成容器集合元素的遍历。
  • 抽象聚合容器(Aggregate)角色:提供一个接口给所有的具体聚合容器使用,提供一个可以创建迭代器的方法createIterator(), 该方法必须返回一个迭代器对象。
  • 具体聚合容器(ConcreteAggregate)角色:持有一个对象的集合,实现抽象容器接口的方法,该方法负责实例化并返回一个具体的迭代器实例。

示例代码:

public interface Aggregate {
    /**返回一个迭代器对象*/
    public Iterator createIterator();

    public void add(Object object);
    public void remove(Object object);
}
public class ConcreteAggregate implements Aggregate {
    private List<Object> objectList = new ArrayList<>();

    @Override
    public Iterator createIterator() {
        //创建并返回一个迭代器对象
        return new ConcreteIterator(objectList);
    }

    @Override
    public void add(Object object) {
        objectList.add(object);
    }

    @Override
    public void remove(Object object) {
        objectList.remove(object);
    }
}
public interface Iterator {
    /**是否有下一个元素*/
    public boolean hasNext();
    /**返回下一个元素*/
    public Object next();
    /**移除当前元素*/
    public void remove();
}
public class ConcreteIterator implements Iterator {
    private List<Object> objectList = new ArrayList<>();
    private int cursor = 0;

    public ConcreteIterator(List<Object> objectList) {
        this.objectList = objectList;
    }

    @Override
    public boolean hasNext() {
        return cursor < objectList.size();
    }

    @Override
    public Object next() {
        if (cursor < objectList.size()) {
            return objectList.get(cursor++);
        }
        return null;
    }

    @Override
    public void remove() {
        objectList.remove(cursor);
    }
}
public class Client {
    public static void main(String[] args) {
        //构造一个聚合对象并添加一些数据
        Aggregate aggregate = new ConcreteAggregate();
        aggregate.add("aaa");
        aggregate.add("bbb");
        aggregate.add("ccc");

        //获取聚合对象的迭代器进行遍历操作
        Iterator iterator = aggregate.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

  可以看到,在上面代码中具体的聚合对象类ConcreteAggregate没有提供直接访问元素的方法,它内部持有一个元素集合,但是对元素集合的访问权交给了Iterator对象。在具体的迭代器实现类ConcreteIterator中,实现了Iterator接口的三个必要方法hasNext()、next()、remove(), 这样在客户端Client代码中对聚合对象中元素的遍历操作就可以借由对应的Iterator来完成。

  选代器模式给你提供了一种方法:可以顺序访问一个聚合对象中的元素,而又不暴露其内部的表示。

  选代器模式给你带来的另一个重要的优点是,迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得更简洁,也可以让聚合更专注在它所应该专注的事情上面(也就是管理对象集合),而不必去理会遍历的事情。职责更加清晰明确,迭代器模式很好的遵循了单一职责的OO设计原则。

Java API中的迭代器模式

  在java最新版本的jdk当中,几乎所有的集合对象都已经实现了迭代器模式。在java中所有的集合对象的最顶层接口都是Collection, 我们常见的List、Set、Queue、Stack、Vector等都实现了Collection接口:
Collection

我们看一下Collection接口的源码:

public interface Collection<E> extends Iterable<E> {
    int size();

    boolean isEmpty();

    boolean contains(Object var1);

    Iterator<E> iterator();

    Object[] toArray();

    <T> T[] toArray(T[] var1);

    boolean add(E var1);

    boolean remove(Object var1);

    boolean containsAll(Collection<?> var1);

    boolean addAll(Collection<? extends E> var1);

    boolean removeAll(Collection<?> var1);

    default boolean removeIf(Predicate<? super E> var1) {
        Objects.requireNonNull(var1);
        boolean var2 = false;
        Iterator var3 = this.iterator();

        while(var3.hasNext()) {
            if(var1.test(var3.next())) {
                var3.remove();
                var2 = true;
            }
        }

        return var2;
    }

    boolean retainAll(Collection<?> var1);

    void clear();

    boolean equals(Object var1);

    int hashCode();

    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }

    default Stream<E> stream() {
        return StreamSupport.stream(this.spliterator(), false);
    }

    default Stream<E> parallelStream() {
        return StreamSupport.stream(this.spliterator(), true);
    }
}

可以看到Collection接口继承了Iterable接口,并且我们看到了Iterator的影子,再看一下Iterable接口的源码:

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

    default void forEach(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        Iterator var2 = this.iterator();

        while(var2.hasNext()) {
            Object var3 = var2.next();
            var1.accept(var3);
        }

    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(this.iterator(), 0);
    }
}

在Iterable接口当中有一个方法Iterator<T> iterator();没错,这个就是创建迭代器的方法。再看一下它的Iterator接口:

public interface Iterator<E> {
    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> var1) {
        Objects.requireNonNull(var1);

        while(this.hasNext()) {
            var1.accept(this.next());
        }

    }
}

  可以看到Iterator接口跟我们前面提到的迭代器模式中的示例代码相差无几,不过java中的迭代器接口都加了泛型,以便任何集合的实现类都可以方便的使用它。

  因此在java中所有的Collection接口的集合子类,都可以像下面的代码这样使用迭代器去遍历:

  ArrayList<String> list = new ArrayList<>();
  list.add("1111");
  list.add("2222");
  list.add("3333");

  Iterator<String> iterator = list.iterator();
  while (iterator.hasNext()) {
      System.out.println(iterator.next());
  }

  当然,从JDK1.5开始,都不需要你显示的使用Iterator接口,提供了新的for/in或者叫for/each写法,也就是我们日常写代码时常用的写法:

 ArrayList<String> list = new ArrayList<>();
 list.add("1111");
 list.add("2222");
 list.add("3333");
 for (String str : list) {
     System.out.println(str);
 }

  看到这段代码,你可能觉得,哦,原来迭代器就是for循环啊,so easy? 没错,我们几乎每天都在用迭代器模式!只是我们感知不到,因为JDK已经为我们提供好了良好的封装。除了迭代器,java的Collection接口也封装了很多其它操作,也是经常使用的。

  有些书中提到,迭代器模式已经是一个没落的设计模式,因为许多友好的高级程序设计语言当中已经默认实现了迭代器模式,甚至想将它从设计模式中除名。但我不这样认为,作为一个几乎被所有高级程序设计语言集成到API当中的设计模式,迭代器模式一直延续使用至今,这说明它的优势带来的方便程度是很大的。如果我们现在想要对外部屏蔽一个集合元素的遍历细节,还是可以使用迭代器的,只不过机会比较少,也很少有人会去自己实现。但这并不意味着迭代器模式是一个没用的模式,相反它十分重要。



参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值