今天的博客主题
设计模式 ——》 设计模式之迭代器模式
迭代器模式 LP (Lterator Pattern)
定义
提供一种顺序访问集合/容器对象元素的方法,而无需暴露集合内部的表示。
可以为不同的容器提供一致的遍历行为,不用关系其容器内容元素的组成结构。
其本质是抽离集合对象迭代行为到迭代器中,提供一致的访问接口。
应用场景
1)访问一个集合对象的内容而无需暴露它的内部表示。
2)为遍历不同的集合结构提供一个统一的访问接口。
优点
1)多态迭代,为不同的聚合结构提供一致的遍历接口,也就是一个迭代接口可以访问不同的集合对象。
2)简化集合对象接口,迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中,使集合对象无需关心具体迭代行为。
3)元素迭代功能多样化,每个集合对象都可以提供一个或多个不同的迭代器,使的同种元素聚合结构可以有不同的迭代行为。
4)解耦迭代与集合,迭代器模式封装了具体的迭代算法,算法的变化不会影响集合对象的结构。
缺点
1)对于比较简单的集合遍历(像数组或者有序列表),使用迭代器方式遍历将会变得繁琐。
日常不会自己写迭代器使用,除非需要定制一个自己实现的数据结构对应的迭代器。
2)抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展。
不过开源框架中的API已足够使用。
源码中的应用
// jdk 集合迭代器,有两个主要的方法:next() 和 hasNext()
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
// 实现类
public abstract class AbstractList ... {
...
private class Itr implements Iterator<E> {
// 后续调用next返回的元素的索引。
int cursor = 0;
// 最近一次调用next或previous返回的元素的索引。如果通过调用remove删除此元素,则重置为-1。
int lastRet = -1;
// 迭代器认为支持列表应该具有的modCount值。如果违反此期望,则迭代器已检测到并发修改。
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();
}
}
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();
}
}
...
// 其还有对 Itr 进行扩展的 ListItr
}
// mybatis api 也是实现了 jdk 的 iterator
public class DefaultCursor<T> implements Cursor<T> { ... }
public interface Cursor<T> extends Closeable, Iterable<T> { ... }
代码示例
通过框架提供的 API 可以看出主要包含以下几个角色:
抽象迭代器(Iterator):负责定义访问和遍历元素的接口。
具体迭代器(ConcreteIterator):负责进行具体的元素遍历。
抽象的容器(Aggregate):提供具体迭代器的接口。
具体的容器(ConcreteAggregate):创建具体的迭代器。
根据这些定义,参考源码,自己实现一个迭代器
// 自定义抽象迭代器
interface Iterator<E>{
E next();
boolean hasNext();
}
class ConcreteIterator<E> implements Iterator<E>{
List<E> list;
int cursor;
E element;
public ConcreteIterator(List list){
this.list = list;
}
@Override
public E next() {
System.out.println("当前位置索引:" + cursor);
element = list.get(cursor);
cursor ++;
return element;
}
@Override
public boolean hasNext() {
return cursor != list.size();
}
}
// 集合元素
class Element{
private String name;
public Element(String name){
this.name = name;
}
public String getName(){
return name;
}
}
// 集合容器接口
interface Aggregate{
void add(Element e);
void remove(Element e);
Iterator<Element> iterator();
}
// 具体的集合容器
class ConcreteAggregate implements Aggregate{
private List list;
public ConcreteAggregate(){
this.list = new ArrayList();
}
@Override
public void add(Element e) {
list.add(e);
}
@Override
public void remove(Element e) {
list.remove(e);
}
@Override
public Iterator<Element> iterator() {
return new ConcreteIterator(list);
}
}
// 客户端调用
public class IteratorPattern {
public static void main(String[] args) {
Element e1 = new Element("java");
Element e2 = new Element("ai");
Element e3 = new Element("python");
Element e4 = new Element("vue");
Aggregate ca = new ConcreteAggregate();
ca.add(e1);
ca.add(e2);
ca.add(e3);
ca.add(e4);
iteratorList(ca);
}
public static void iteratorList(Aggregate aggregate){
Iterator<Element> iterator = aggregate.iterator();
while (iterator.hasNext()){
Element element = iterator.next();
System.out.println("you like " + element.getName());
}
}
}
// 输出结果
当前位置索引:0 > you like java
当前位置索引:1 > you like ai
当前位置索引:2 > you like python
当前位置索引:3 > you like vue
迭代器模式其本质就是 next() 和 hasNext() 进行操作。