1、迭代器 Iterator模式 一个一个遍历 行为型设计模式
1、Iterator模式
迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器(container,例如链表或者阵列)上遍访的接口,设计人员无需关心容器的内容。
Iterator模式 - 一个一个遍历,我们将学习从含有多个元素的集合中将各个元素逐一取出来的iterator模式。
导学
对于数组我们使用的是下标来进行处理的:
1 int array[] = new int[3];
2 for (int i = 0; i < array.length; i++) {
3 System.out.println(array[i]);
4 }
对ArrayList的处理
1 List<String> list = new ArrayList<String>();
2 for(int i = 0 ; i < list.size() ; i++){
3 String string = list.get(i);
4 }
将这里的i的作用抽象化,通用化后形成的模式,在设计模式中称为iterator模式。
Iterator模式用于在数据集合按照顺序遍历集合。英语单词iterate有反复做某件事情的意思,汉语称为“迭代器”。
2、实例程序
这段程序的作用是将书(Book)放置到书架(BookShelf)中,并将书的名字按照顺序显示出来.
类和接口的意义:
Aggregate: 表示集合的接口
Iterator: 遍历集合的接口
BookShelf: 表示书架的类
BookShelfIterator: 遍历书架的类
Book: 表示书的类
2.1 Aggregate接口
package cn.design.iterator; /** * @author lin * @version 1.0 * @date 2020-07-13 14:16 * @Description 表示集合的接口 */ public interface Aggregate { /** * 在Aggregate接口中声明的方法只有一个一-iterator 方法。该方法会生成-一个用于遍历集合的迭代器。 * 想要遍历集合中的元素时,可以调用iterator方法来生成一一个实现了Iterator接口的类的实例。 */ public abstract Iterator iterator(); }
2.2 Iterator接口
package cn.design.iterator; /** * @author lin * @version 1.0 * @date 2020-07-13 14:30 * @Description TODO */ public interface Iterator { /** * 判断是否存在下一-个元素 * 当集合中存在下一个元素 * 时,该方法返回true;当集合中不存在下一个元素,即已经遍历至集合末尾时,该方法返回 * false。hasNext 方法主要用于循环终止条件。 * * @return */ public abstract boolean hasNext(); /** * 取下一个元素 * 该方法返回的是集合 * 中的一一个元素。但是,next方法的作用并非仅仅如此。为了能够在下次调用next方法时正确地返 * 回下一个元素,该方法中还隐含着将迭代器移动至下一个元素的处理。说“隐含”,是因为 * Iterator接口只知道方法名。想要知道next方法中到底进行了什么样的处理,还需要看一下实 * 现了Iterator接口的类( BookShelfIterator)。这样,我们才能看懂next方法的作用。 * * @return object */ public abstract Object next(); }
2.3 Book类
package cn.design.iterator; /** * @author lin * @version 1.0 * @date 2020-07-13 14:33 * @Description TODO */ public class Book { private String name; public Book(String name) { this.name = name; } public Book() { } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "Book{" + "name='" + name + '\'' + '}'; } }
2.4 BookShelf类
package cn.design.iterator; /** * @author lin * @version 1.0 * @date 2020-07-13 19:00 * @Description TODO */ public class BookShelf implements Aggregate { /** * 这个书架中定义了books字段,它是Book类型的数组。该数组的大小( maxsize )在生成 * BookShelf的实例时就被指定了。之所以将books字段的可见性设置为private,是为了防止. * 外部不小心改变了该字段的值。 */ private Book[] books; private int last = 0; public BookShelf(int maxSize) { this.books = new Book[maxSize]; } public Book getBookAt(int index) { return books[index]; } public void appendBook(Book book) { this.books[last] = book; last++; } public int getLength() { return last; } /** * BookShelf类对应的Iterator。当外部想要遍历书架时,就会调用这个方法。 * * @return Iterator 实现类 */ @Override public Iterator iterator() { return new BookShelfIterator(this); } }
2.5 BookShelfIterator 类
package cn.design.iterator; /** * @author lin * @version 1.0 * @date 2020-07-13 19:03 * @Description TODO */ public class BookShelfIterator implements Iterator { /** * bookShelf字段表示BookShelfIterator所要遍历的书架。 */ private BookShelf bookShelf; /** * index字段表示迭代器当前所指向的书的下标。 */ private int index; /** * @param bookShelf */ /** * 构造函数会将接收到的BookShelf的实例保存在bookShelf字段中,并将index初始化为0。 * * @param bookShelf */ public BookShelfIterator(BookShelf bookShelf) { this.bookShelf = bookShelf; this.index = 0; } /** * hasNext方法是Iterator接口中所声明的方法。该方法将会判断书架中还有没有下一-本书, . * 如果有就返回true,如果没有就返回false。而要知道书架中有没有下一本书,可以通过比较 * index和书架中书的总册数( bookShelf . getLength ()的返回值)来判断。 * * @return */ @Override public boolean hasNext() { return index < bookShelf.getLength(); } /** * next方法会返回迭代器当前所指向的书( Book的实例),并让迭代器指向下一-本书。它也是 * Iterator接口中所声明的方法。next方法稍微有些复杂,它首先取出book变量作为返回值, * 然后让index指向后面- -本书。 * 如果与本章开头的for语句来对比,这里的“让index指向后面一-本书”的处理相当于其中 * 的i++,它让循环变量指向下一个元素。 * * @return */ @Override public Object next() { Book bookAt = bookShelf.getBookAt(index); index++; return bookAt; } }
2.6 TestMain测试类
package cn.design.iterator; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.List; /** * @author lin * @version 1.0 * @date 2020-07-13 19:05 * @Description TODO */ public class TestMain { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(4); bookShelf.appendBook(new Book("环游世界80天")); bookShelf.appendBook(new Book("圣经")); bookShelf.appendBook(new Book("灰姑娘")); bookShelf.appendBook(new Book("阿拉丁神灯")); Iterator it = bookShelf.iterator(); while (it.hasNext()) { Book book = (Book) it.next(); System.out.println(book.toString()); } // ArrayList<String> list = new ArrayList<>(); // list.add("aaaa"); // list.add("bbbb"); // list.add("cccc"); // list.add("dddd"); // java.util.Iterator<String> it2 = list.iterator(); // while (it2.hasNext()) { // System.out.println("it2.next() = " + it2.next()); // } } }
运行结果如下:
Book{name='环游世界80天'}
Book{name='圣经'}
Book{name='灰姑娘'}
Book{name='阿拉丁神灯'}
通过bookShelf. iterator()得到的it是用于遍历书架的Iterator实例。while部分的条件当然就是it.hasNext()了。只要书架上有书,while 循环就不会停止。然后,程序会通过it.next()一本一本地遍历书架中的书。
3、Iterator模式中的各个角色
读完示例程序,让我们来看看Iterator模式中的登场角色。
1、Iterator (迭代器)
该角色负责定义按顺序逐个遍历元素的接口( API)。在示例程序中,由Iterator接口扮演这个角色,它定义了hasNext和next两个方法。其中,hasNext 方法用于判断是否存在下一个元素,next方法则用于获取该元素。
2、Concretelterator (具体的迭代器)
该角色负责实现Iterator角色所定义的接口( API)。在示例程序中,由BookShelfIterator类扮演这个角色。该角色中包含了遍历集合所必需的信息。在示例程序中,BookShelf类的实例保存在bookShelf字段中,被指向的书的下标保存在index字段中。
3、Aggregate (集合)
该角色负责定义创建Iterator角色的接口( API)。这个接口( API)是-一个方法,会创建出“按顺序访问保存在我内部元素的人”。在示例程序中,由Aggregate接口扮演这个角色,它里面定义了iterator 方法。
4、ConcreteAggregate ( 具体的集合)
该角色负责实现Aggregate角色所定义的接口(API)。它会创建出具体的Iterator角色,即Concretelterator角色。在示例程序中,由BookShelf类扮演这个角色,它实现了iterator 方法。
4、扩展思路的要点
4.1、为何iterator必不可少?
while (it.hasNext()) {
Book book = (Book) it.next();
System.out.println(book.toString());
}
上述只使用了Iterator的hasNext方法和next方法,并没有调用BookShelf的方法。也就是说,这里的while循环并不依赖于BookShelf的实现。
如果编写BookShelf的开发人员决定放弃用数组来管理书本,而是用java.util. vector取而代之,会怎样呢?不管BookShelf如何变化,只要BookShelf的iterator方法能正确地返回Iterator的实例(也就是说,返回的Iterator类的实例没有问题,hasNext 和next方法都可以正常工作),即使不对上面的while循环做任何修改,代码都可以正常工作。
这对于BookShelf的调用者来说真是太方便了。设计模式的作用就是帮助我们编写可复用的类。所谓“可复用”,就是指将类实现为“组件”,当一个组件发生改变时,不需要对其他的组件进行修改或是只需要很小的修改即可应对。
这样也就能理解为什么在示例程序中iterator方法的返回值不是BookShelfIterator类型而是Iterator类型了(代码清单1-6)。这表明,这段程序就是要使用Iterator的方法进行编程,而不是BookShelfIterator的方法。
4.2、难以理解的抽象类和接口
难以理解抽象类和接口的人常常使用ConcreteAggregate角色和ConcreteIterator角色编程,而不使用Aggregate接口和Iterator接口,他们总想用具体的类来解决所有的问题。
但是如果只使用具体的类来解决问题,很容易导致类之间的强耦合,这些类也难以作为组件被再次利用。为了弱化类之间的耦合,进而使得类更加容易作为组件被再次利用,我们需要引入抽象类和接口。
4.3、Java中ArrayList源码
私有的内部类Itr
/** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } ArrayList内部: /** * Returns an iterator over the elements in this list in proper sequence. * * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>. * * @return an iterator over the elements in this list in proper sequence */ public Iterator<E> iterator() { return new Itr(); }
获取到Iterator,就可以使用boolean hasNext();和E next();俩个方法进行遍访操作。
Java源码:
发哥讲上传到码云上:
https://gitee.com/naimaohome/talk_about_fage.git
发哥讲
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注公众号