关于迭代器(Iterator)
迭代器这个名词想来我们已经不陌生了,在集合中我们就学习过迭代器,并掌握了如何使用迭代器来遍历集合中的元素。
例如:
List<String> list = new ArrayList<>();
list.add("anc");
list.add("cvl");
list.add("uio");
list.add("ope");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.print(list.next());
}
输出:anc cvl uio ope
实际上,迭代器就是反复同一件事的一个循环,就如同for(int i = 0; i < 100; i++)中的i++,只要符合条件就加1,直到遍历完每一个元素后就退出,而迭代器模式(Iterator模式)的思想也就是来自于此。迭代器模式是一种没落的模式,除非产品性质的需要,否则不会单独去写一个迭代器模式(Java中的集合就对迭代器模式进行优秀的封装,基本上不用认为扩展,都能满足我们的需求)。我们可以看看迭代器的定义:迭代器提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的细节。通俗点说,迭代器的诞生就是为遍历容器(容器就是能持有对象的集合,如Collection,Set)中的元素服务的。
迭代器模式——你在不在?我要揍你一顿
举一个例子,详细讲讲什么是迭代器模式
在一个风和日丽的早晨,阳光明媚,春暖花开,打开古老的藏书阁,我要找一本心仪的古书陶冶一下情操,可是让我很郁闷的是,由于时间太久没人整理,很多书都凌乱的放在书架上,所以我费了老大的劲儿才把书籍找到。我就在想,要是有一个这样的物件就好了,它能在我想要从一堆杂乱无章的事物中寻找某一件东西时,它能按整齐的把它排列(排列顺序自己选择)好,这样我找的时候不就容易多了,我冥思苦想很久,找了很多上古典籍查阅(《设计模式之禅》,《图解设计模式》)中找到了答案,那就是上古秘书——迭代器模式。
现有一个书架BookShelf,有一堆书Book,我想把书放到书架上,并按照书的名字按顺序显示出来。看一下示例图
程序的类图如下
类和接口
逐步剖析(请直接看代码)
Aggregate接口:表示集合的接口,相当于Java集合中Collection,List等集合接口
/**
* 表示集合的接口
*/
public interface Aggregate {
/**
* 该方法用于生成一个遍历结合的迭代器
*
* 在遍历集合中的元素时吗,调用iterator方法生成一个实现了Iterator接口的类的实例
* @return
*/
public abstract Iterator iterator();
}
Iterator接口:表示用于遍历集合的接口
/**
* 用于遍历集合的接口:遍历集合中的元素,其作用相当于循环语句中的循环变量
*/
public interface Iterator {
/**
* 判断是否存在下一个元素
* @return
*/
public abstract boolean hasNext();
/**
* 返回集合中的一个元素
* @return
*/
public abstract Object next();
}
BookShelf类:实现了Aggregate接口,表示一个书籍的集合,可以看成Java集合框架中的实现类
/**
* 表示书架类,可以看成是一个书的集合,所以需要实现Aggregate接口
*/
public class BookShelf<T> implements Aggregate {
private Book[] books;// 可见性设置为private,避免不小心篡改
private int last;//标识最后一本书的索引
public BookShelf(int maxsize) {
this.books = new Book[maxsize];// 生成BookShelf实例的时候指定了books的大小
}
//根据索引得到书籍
public Book getBookAt(int index) {
return books[index];
}
//向书架上添加书籍
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
//返回书籍的数量
public int getLength() {
return last;
}
@Override
public Iterator iterator() {
return new BookShelfIterator<T>(this);
}
}
BookShelfIterator类:Iterator的实现类
/**
* 遍历书籍的迭代器实现(Iterator)类
* @param <T>
*/
public class BookShelfIterator<T> implements Iterator {
private BookShelf<T> bookShelf;//表示BookShelfIterator要遍历的书架
private int index;//表示迭代器当前指向的书的索引
//初始化
public BookShelfIterator(BookShelf<T> bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
<!-- if (index < bookShelf.getLength()) {//如果当前索引小于书架书籍的数量,表名下一本书还存在,继续遍历
return true;
} else {
return false;
} -->
return index != bookShelf.getLength;
}
/**
* next()方法有两个作用:
* >返回当前索引对应的书籍实例
* >并将index指向下一个元素 >>>> 这和for循环中的i++很像
*/
@Override
public Object next() {
Book book = bookShelf.getBookAt(index);//将对应索引的书一一返回
index++;
return book;
}
}
Book类:书籍类(实体类)
public class Book {
private String name;
public Book(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book [name=" + name + "]";
}
}
下面是Main方法
public class Main {
public static void main(String[] args) {
BookShelf<Book> bookShelf = new BookShelf<>(4);
bookShelf.appendBook(new Book("雪国"));
bookShelf.appendBook(new Book("山中古音"));
bookShelf.appendBook(new Book("天堂"));
bookShelf.appendBook(new Book("一个人的路"));
Iterator iterator = bookShelf.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
运行结果:
Book [name=雪国]
Book [name=山中古音]
Book [name=天堂]
Book [name=一个人的路]
集合框架中的迭代器模式(源码解析)
上面是我们自己写的迭代器模式,用我们自己的想法实现了一个简单的迭代器,怎么样,你对迭代器了解有多少了呢,如果你还是不清楚,那我们再来看看Java集合框架是如何运用迭代器的。我将主要以源码的方式解析,你会发现其实和我上面写的差不多。
我就使用List接口进行解析,List定义了如下方法
Iterator<E> iterator();
Iterator接口:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
//该方法与函数式接口有关(lambda表达式)
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
以List的一个实现类ArrayList来看看如何实现的
public Iterator<E> iterator() {
return new Itr();
}
//Itr是AbstractList的一个成员内部类(其实ArrayList也有一个Itr的成员内部类
只不过这是优化版本)
private class Itr implements Iterator<E> {
int cursor; //表示下一个要访问的元素的索引
int lastRet = -1; //表示上一个元素的索引
int expectedModCount = modCount;//表示对ArrayList修改次数的期望值,它的初始值为modCount(modCount是AbstractList类中的一个成员变量)
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();
}
}
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();
}
}
深入研究一下
OK,上面就是迭代器在集合框架中的使用,现在我们或许就理解了为什么在学习集合的迭代器器时说,我们是去获取一个迭代器,而并非是我们new一个迭代器对象,下面我们来看看迭代器模式的类图,并分析一下我们为什么要使用迭代器
- Iterator(迭代器):该角色定义了顺序逐个遍历元素的接口
- ConcreteIterator(具体的迭代器):实现Iterator接口
- Aggregate(集合):定义创建Iterator角色的接口
- ConcreteAggregate(具体的集合):该角色负责实现Aggregate所定义的接口
看了上面介绍的迭代器模式,我们或许头有点晕,既然同是迭代,为什么我们不去使用更为普通的迭代方式,如for循环呢,非要用这种复杂的设计模式干什么,其实回到前面迭代器模式的定义:迭代器模式提供一种顺序访问一个聚合对象中各个元素的方法,而又不暴露该对象的内部实现。
举个例子:我是车站的售票员,对于我来说,我只是负责售票的,其他的事情我一概不管,对于我来说,只有一个原则,那就是只有买了票才能乘车,不管你是中国人外国人,不管你是小偷还是杀人犯,只要你没买票就不能乘车,我不用管乘车对象是什么,我只管你有没有买票。
如同上面举的那个例子,这里只是用了Iterator的hasNext和next方法,并没有调用BookShelf的方法,也就是说,while循环并不依赖于BookShelf的实现。不管BookShelf如何变化,只要BookShelf的iterator方法能返回正确的Iterator实例,即使不对while循环做任何修改,都可以正常工作。设计模式的作用就是帮助我们编写可复用的类。而可复用的含义就是将类视为“组件”,当一个组件方法发生变化时,不需要对其他组件进行修改或者只需要进行很小的修改就可以了,大大提高了代码的重用率。
while(iterator.hasNext()){
System.out.println(iterator.next());
}
结语
迭代器模式是一种古老的设计模式,也是一种非常常见的设计模式,它已经完美的镶嵌于聚合关系中,如Java的集合框架,几乎每一个集合类都对迭代器模式进行了封装。正是由于它的普遍性,所以很多大佬都提出将迭代器模式从23中设计模式中删除。不管迭代器模式在未来是否还能存在于设计模式家族中,我们只需要知道,应用之广泛则证明其价值,所以值得我们去学习研究它的思想精髓。
推荐书籍与网站:
《图解设计模式》 《大话设计模式》 《设计模式》 《Java设计模式》