Iterator模式


title: Iterator模式
tag: 笔记 设计模式

image-20231017143616583

Iterator模式

在Java中我们可以用下面的for循环的方式遍历一个数组:

for (int i = 0; i < nums.length; i++) {
     System.out.println(nums[i]);
}

这是我们非常熟悉的一种遍历元素的一种方式,其中i++的作用是时i的值循环之后自增,这样就能够将数组从头到尾遍历一遍。

我们可以将这里的i的作用抽象化,通用化后形成的模式在设计模式中称为**Iterator模式**。Iterator模式用于在数据集合中按照顺序遍历集合,也叫做迭代器模式

示例程序

这段示例我们将书(Book)放置到书架(BookShelf)中,并可以将书的名字顺序输出。

示意图:

image-20231017145524586

设计类图:

image-20231017145649163

该示例程序由以下部分组成:

  • Aggregate:表示集合的接口
  • Iterator:遍历集合的接口
  • Book:书的类
  • BookShelf:书架的类
  • BookShelfIterator:遍历暑假的类

Aggregate接口

该接口在Java中用Iterable接口表示,意为可迭代的,是在集合的接口Collection中继承的接口。

实现该接口的类是集合类

该接口的方法很简单:

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

作用是返回一个Iterator迭代器,用于遍历集合。

Iterator接口

Iterator接口用于遍历集合中的元素,其作用相当于循环遍历中的循环变量(i),该接口的定义方式很多,我们仅仅使用最简单的接口:

package Iterator;

public interface Iterator<T> {

    boolean hasNext();

    T next();
}

该接口包含了实现遍历的最基本的两个方法:

  • hasNext:是否存在下一个元素,返回布尔值。相当于for循环中的循环执行条件(i < nums.lenth)
  • next:返回当前元素,并将迭代器移动到下个位置。相当于for循环中的nums[i]i++

Book类

表示书的类,也就是集合中的元素。该类的作用仅仅是输出自己的名字。

public class Book {
    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

BookShelf类

表示书架的类,也就是需要将该类作为集合处理,需要实现Aggregate接口,目前该类我们使用Book数组来实现。

public class BookShelf implements Aggregate<Book>{
    Book[] books;
    int last = 0;

    public BookShelf(int shelfSize){
        books = new Book[shelfSize];
    }

    public void appendBook(Book book){
        books[last++] = book;
    }

    public int getLength(){
        return last;
    }

    public Book getBookAt(int index) {
        return books[index];
    }
	
    /**
     * 获得该类的迭代器
     * @return
     */
    @Override
    public Iterator<Book> iterator() {
        return new BookShelfIterator(this);
    }
}

该类实现了一些集合类的基础操作,并且实现了iterator方法获取该类的迭代器,目前我们返回的BookShelfIterator类还未实现,因此我们下一步需要实现该类的迭代器,也就是BookShelfIterator

BookShelfIterator

该类用于实现遍历BookShelf,我们需要实现Iterator接口的hasNextnext方法:

public class BookShelfIterator implements Iterator<Book>{
    private int index;
    private BookShelf bookShelf;
    public BookShelfIterator(BookShelf bookShelf){
        this.bookShelf = bookShelf;
        index = 0;
    }
    @Override
    public boolean hasNext() {
        return index < bookShelf.getLength();
    }

    @Override
    public Book next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

测试

至此,我们这个简单的迭代器示例程序就基本实现了,我们写一个main方法对该方法进行测试:

public static void main(String[] args) {
    BookShelf bookShelf = new BookShelf(5);
    bookShelf.appendBook(new Book("罪与罚"));
    bookShelf.appendBook(new Book("月亮与六便士"));
    bookShelf.appendBook(new Book("罗生门"));
    bookShelf.appendBook(new Book("局外人"));
    Iterator<Book> iterator = bookShelf.iterator();
    while(iterator.hasNext()){
        System.out.println(iterator.next().getName());
    }
}

这里我们实例化一个容量为5书架,并向其中添加了四本书。我们获取了该类的迭代器对象,并将书架中的书名输出:

罪与罚
月亮与六便士
罗生门
局外人

可以看到我们没有使用for循环而实现了书架的遍历。

Iterator模式中的角色

  • Iterator(迭代器)

该角色负责定义按顺序逐个遍历元素的接口(API)。在示例程序中,由Iterator接口扮演这个角色,它定义了hasNextnext两个方法。其中,hasNext方法用于判断是否存在下一个元素,next方法则用于获取该元素。

  • ConcreteIterator(具体的迭代器)

该角色负责实现Iterator角色所定义的接口(API)。在示例程序中,由BookShelfIterator类扮演这个角色。该角色中包含了遍历集合所必需的信息。在示例程序中,BookShelf类的实例保存在bookShelf字段中,被指向的书的下标保存在index字段中。

  • Aggregate(集合)

该角色负责定义创建Iterator角色的接口(API)。这个接口(API)是一个方法,会创建出“按顺序访问保存在我内部元素的人”。在示例程序中,由Aggregate接口扮演这个角色,它里面定义了iterator方法。

  • ConcreteAggregate(具体的集合)

该角色负责实现Aggregate角色所定义的接口(API)。它会创建出具体的Iterator角色,即Concretelterator角色。在示例程序中,由BookShelf类扮演这个角色,它实现了iterator方法。

扩展思路

无论实现如何变化,都可以使用Iterator

我们为何一定要引入复杂的Iterator模式而不是简单的for循环遍历呢?

一个非常重要的理由就是:Iterator可以将遍历和实现分离开

while(iterator.hasNext()){
    System.out.println(iterator.next().getName());
}

可以看到,这段遍历的代码仅仅使用了iterator的两个方法而没有调用BookShelf的方法。也就是这里的while循环并不依赖于BookShelf的实现

当我们将BookShelf中存放book的容器从数组更换为集合或者其它容器时,只要**Iterator方法获取到的迭代器能够正常工作**,即使不对现有的循环进行改变,循环也同样能够正常工作。

这样就可以极大的方便重构。当我们对BookShelf重构时,我们仅仅只需要保证它的迭代器能够正常工作,我们就无需对遍历BookShelf的代码进行修改

比如,我们可以将我们示例程序中保存book的容器更换为ArrayList:

public class BookShelf implements Aggregate<Book>{
    ArrayList<Book> books;
    int last = 0;

    public BookShelf(int shelfSize){
        books = new ArrayList(shelfSize);
    }

    public void appendBook(Book book){
        books.add(book);
        last++;
    }

    public int getLength(){
        return last;
    }

    public Book getBookAt(int index) {
        return books.get(index);
    }

    @Override
    public Iterator<Book> iterator() {
        return new BookShelfIterator(this);
    }
}

我们还是运行原来的测试代码,输出结果相同。

从这个例子我们可以使用迭代器模式后重构会变得非常简单。

Aggregate和Iterator的对应

BookShelfIterator类知道BookShelf是如何实现的。也正是因为如此,我们才能调用用来获取下一本书的getBookAt方法。

也就是说,迭代器的实现依赖于集合类,当集合类的API发生改变时比如(getBookAt),我们也需要修改迭代器的类,保证迭代器能够返回的正确值

多个Iterator

将遍历功能置于Aggregate角色之外Iterator模式的一个特征。根据这个特征,可以针对个ConcreteAggregate角色编写多个Concretelterator角色。

例如java的集合类库中ArrayList就实现了两个迭代器:

  • Itr
private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException(e);
            }
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
  • ListItr
private class ListItr extends Itr implements ListIterator<E> {
        ListItr(int index) {
            cursor = index;
        }

        public boolean hasPrevious() {
            return cursor != 0;
        }

        public E previous() {
            checkForComodification();
            try {
                int i = cursor - 1;
                E previous = get(i);
                lastRet = cursor = i;
                return previous;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException(e);
            }
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor-1;
        }

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.set(lastRet, e);
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                AbstractList.this.add(i, e);
                lastRet = -1;
                cursor = i + 1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

ListItrItr的升级版,多了一些操作。

相关的设计模式

  • Visitor模式:

Iterator是从集合中一个一个取出元素进行遍历,但是并没有在Iterator接口中声明对取出的元素进行何种处理
Visitor模式则是在遍历元素集合的过程中,对元素进行相同的处理。在遍历集合的过程中对元素进行固定的处理是常有的需求。Visitor模式正是为了应对这种需求而出现的。在访问元素集合的过程中对元素进行相同的处理,这种模式就是Visitor模式。

  • Factory Method模式:

我们可能会使用Factory Method来生成Iterator示例。
on ex) {
throw new ConcurrentModificationException();
}
}
}


`ListItr`是`Itr`的升级版,多了一些操作。



## 相关的设计模式

* Visitor模式:

`Iterator`是从集合中一个一个取出元素进行遍历,但是**并没有在`Iterator`接口中声明对取出的元素进行何种处理**。
Visitor模式则是在遍历元素集合的过程中,**对元素进行相同的处理**。在遍历集合的过程中对元素进行固定的处理是常有的需求。Visitor模式正是为了应对这种需求而出现的。在访问元素集合的过程中对元素进行相同的处理,这种模式就是`Visitor`模式。

* `Factory Method`模式:

我们可能会使用`Factory Method`来生成Iterator示例。
  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值