什么是迭代器模式?
迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。
没有迭代器的时代,我们是怎么遍历容器的
for (int i = 0; i< arrayList.size(); ++i) {
System.out .println(arrayList.get(i));
}
我们需要关心容器内部的方法size(),get()
这样用户去使用容器就和容器强耦合了,必须去了解容器的内部。
迭代器解决什么问题?
从定义可见,迭代器模式是为容器而生。
迭代器解决的问题就是在不关心对象内部细节的情况下去遍历容器。
你可以一股脑的将遍历方法塞到容器对象中去;或者根本不去提供什么遍历算法,让使用容器的人自己去实现去吧。这两种情况好像都能够解决问题。
然而在前一种情况,容器承受了过多的功能,它不仅要负责自己“容器”内的元素维护(添加、删除等等),而且还要提供遍历自身的接口;而且由于遍历状态保存的问题,不能对同一个容器对象同时进行多个遍历。第二种方式倒是省事,却又将容器的内部细节暴露无遗。
而迭代器模式的出现,很好的解决了上面两种情况的弊端。先来看下迭代器模式的真面目吧。
迭代器模式应用举例
内部迭代器
这个我就不说了,就是在容器里面实现一个遍历,然后暴露给客户端,客户端调用方法,即以固定的方式遍历容易
优点:遍历的时候,用户不用关心容器的实现,解耦
缺点:太死板,遍历算法不能客户端实现。
外部迭代器
我个人比较推崇外部迭代器
Java Collections容器中就是利用了外部迭代器去进行容器访问的
JDK中提供了一个迭代器接口
public interface Iterator<E> {
boolean hasNext(); E next(); void remove();
}
这个迭代器可以根据不同的容器有不同的内部实现,例如Itr实现了Iterator<E>
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
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();
}
}
这个具体的迭代器类作为一个轻量级容器组合在需要迭代功能的容器中,例如AbstractList中,用户可以通过
public Iterator<E> { return new Itr(); } 方法
得到具体的迭代器,在JVM运行时可以得到具体的迭代器,用户得到具体的迭代器后,就可以通过迭代器接口中提供的方法去操纵容器了
JDK的这种迭代器实现又叫做多态迭代器。
这样用户无需关心容器的内部实现,就可以去访问遍历容器,并且还可以利用自己的遍历算法。
总结一下好处:
1) 与容器的具体实现解耦
2) 遍历算法的在客户端实现,不死板,保持多样性
补充一点东西,最近看多态迭代器,对迭代器内部的checkForComodification感兴趣了,
代码如下
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
代码中,modCount记录了LinkedList结构被修改的次数。Iterator初始化时,expectedModCount=modCount。任何通过Iterator 修改LinkedList结构的行为都会同时 更 新expectedModCount和modCount,使这两个值相等。通过LinkedList对象修改其结构的方法只更新modCount。所以假 设有两个线程A和B。A通过Iterator遍历并修改LinkedList,而B,与此同时,通过对象修改其结构,那么Iterator的相关方法就会 抛出异常。这是相对容易发现的由线程竞争造成的错误。
所以大家在迭代容器的时候,有修改容器的需求,就用迭代器的iter.remove()吧