迭代器模式是一個比較古老的模式凯砍,最常見效的是集合里的迭代器(Iterator)孔轴,肯定都不會陌生。迭代器模式喻杈,提供一種方法順序訪問一個聚合對象中的各種元素,而又不暴露該對象的內(nèi)部表示狰晚。
迭代器的角色構(gòu)成
- 迭代器角色(Iterator):負責定義訪問和遍歷元素的接口筒饰。
- 具體迭代器角色(ConcreteItertor):實現(xiàn)迭代器接口,并要記錄遍歷中的當前位置壁晒。
- 容器角色(Container):負責提供創(chuàng)建具體迭代器角色的接口瓷们。
- 具體容器角色(Concrete Container):實現(xiàn)創(chuàng)建具體迭代器角色的接口, 這個具體迭代器角色與該容器的結(jié)構(gòu)相關(guān)秒咐。
迭代器類圖
迭代器模式類圖
實現(xiàn)代碼
public interface Iterator<T> {
boolean hasNext();
T next();
}
public class ConcreteIterator<T> implements Iterator<T> {
private List<T> list;
private int cursor = 0;
public ConcreteIterator(List<T> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return cursor != list.size();
}
@Override
public T next() {
T obj = null;
if (this.hasNext()) {
obj = this.list.get(cursor++);
}
return obj;
}
}
容器類:
public interface Container<T> {
void add(T obj);
void remove(T obj);
Iterator<T> iterator();
}
public class ConcreteContainer<T> implements Container<T> {
private List<T> list = new ArrayList<>();
@Override
public void add(T obj) {
list.add(obj);
}
@Override
public void remove(T obj) {
list.remove(obj);
}
@Override
public Iterator iterator() {
return new ConcreteIterator(list);
}
}
測試代碼:
public class Client {
public static void main(String args[]) {
Container<String> container = new ConcreteContainer();
container.add("my name");
container.add(" is ");
container.add("****");
Iterator<String> iterator = container.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next());
}
}
}
運行結(jié)果:
my name is ****
在java源碼中常見的就是集合的迭代器谬晕,在Iterator與fast-fail機制中對ArrayList中迭代器源碼進行了分析。ArrayList中是使用內(nèi)部類來實現(xiàn)的迭代器模式携取。除了在集合中攒钳,還有在數(shù)據(jù)庫查詢中Cursor 也是一個迭代器。
迭代器模式的優(yōu)缺點與適用場景
優(yōu)點:
- 它支持以不同的方式遍歷一個聚合對象雷滋,在同一個聚合對象上可以定義多種遍歷方式不撑。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷算法,我們也可以自己定義迭代器的子類以支持新的遍歷方式晤斩。
- 迭代器簡化了聚合類焕檬。由于引入了迭代器,在原有的聚合對象中不需要再自行提供數(shù)據(jù)遍歷等方法尸昧,這樣可以簡化聚合類的設計揩页。
- 在迭代器模式中,由于引入了抽象層烹俗,增加新的聚合類和迭代器類都很方便爆侣,無須修改原有代碼,滿足“開閉原則”的要求幢妄。
** 缺點:** - 由于迭代器模式將存儲數(shù)據(jù)和遍歷數(shù)據(jù)的職責分離兔仰,增加新的聚合類需要對應增加新的迭代器類,類的個數(shù)成對增加蕉鸳,這在一定程度上增加了系統(tǒng)的復雜性乎赴。
- 抽象迭代器的設計難度較大,需要充分考慮到系統(tǒng)將來的擴展潮尝,例如JDK內(nèi)置迭代器Iterator就無法實現(xiàn)逆向遍歷榕吼,如果需要實現(xiàn)逆向遍歷,只能通過其子類ListIterator等來實現(xiàn)勉失,而ListIterator迭代器無法用于操作Set類型的聚合對象羹蚣。在自定義迭代器時,創(chuàng)建一個考慮全面的抽象迭代器并不是件很容易的事情乱凿。
適用場景: - 訪問一個聚合對象的內(nèi)容而無須暴露它的內(nèi)部表示顽素。將聚合對象的訪問與內(nèi)部數(shù)據(jù)的存儲分離咽弦,使得訪問聚合對象時無須了解其內(nèi)部實現(xiàn)細節(jié)。
- 需要為一個聚合對象提供多種遍歷方式胁出。
- 為遍歷不同的聚合結(jié)構(gòu)提供一個統(tǒng)一的接口型型,在該接口的實現(xiàn)類中為不同的聚合結(jié)構(gòu)提供不同的遍歷方式,而客戶端可以一致性地操作該接口全蝶。
但是闹蒜,由于容器與迭代器的關(guān)系太密切了,所以大多數(shù)語言在實現(xiàn)容器的時候都給提供了迭代器裸诽,并且這些語言提供容器和迭代器在絕大多數(shù)情況下就可以滿足我們的需要嫂用,所以現(xiàn)在需要我們自己去實踐迭代器的場景還是比較少的,一般來說只需要使用語言中已有的容器和迭代器就可以了丈冬。