迭代器模式是一個比較古老的模式,最常見效的是集合里的迭代器(Iterator),肯定都不會陌生。迭代器模式,提供一種方法順序訪問一個聚合對象中的各種元素,而又不暴露該對象的內部表示。
迭代器的角色構成
- 迭代器角色(Iterator):負責定義訪問和遍歷元素的接口。
- 具體迭代器角色(ConcreteItertor):實現迭代器接口,并要記錄遍歷中的當前位置。
- 容器角色(Container):負責提供創建具體迭代器角色的接口。
- 具體容器角色(Concrete Container):實現創建具體迭代器角色的接口, 這個具體迭代器角色與該容器的結構相關。
迭代器類圖
迭代器模式類圖
實現代碼
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());
}
}
}
運行結果:
my name is ****
在java源碼中常見的就是集合的迭代器,在Iterator與fast-fail機制中對ArrayList中迭代器源碼進行了分析。ArrayList中是使用內部類來實現的迭代器模式。除了在集合中,還有在數據庫查詢中Cursor 也是一個迭代器。
迭代器模式的優缺點與適用場景
優點:
- 它支持以不同的方式遍歷一個聚合對象,在同一個聚合對象上可以定義多種遍歷方式。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷算法,我們也可以自己定義迭代器的子類以支持新的遍歷方式。
- 迭代器簡化了聚合類。由于引入了迭代器,在原有的聚合對象中不需要再自行提供數據遍歷等方法,這樣可以簡化聚合類的設計。
- 在迭代器模式中,由于引入了抽象層,增加新的聚合類和迭代器類都很方便,無須修改原有代碼,滿足“開閉原則”的要求。
** 缺點:** - 由于迭代器模式將存儲數據和遍歷數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的復雜性。
- 抽象迭代器的設計難度較大,需要充分考慮到系統將來的擴展,例如JDK內置迭代器Iterator就無法實現逆向遍歷,如果需要實現逆向遍歷,只能通過其子類ListIterator等來實現,而ListIterator迭代器無法用于操作Set類型的聚合對象。在自定義迭代器時,創建一個考慮全面的抽象迭代器并不是件很容易的事情。
適用場景: - 訪問一個聚合對象的內容而無須暴露它的內部表示。將聚合對象的訪問與內部數據的存儲分離,使得訪問聚合對象時無須了解其內部實現細節。
- 需要為一個聚合對象提供多種遍歷方式。
- 為遍歷不同的聚合結構提供一個統一的接口,在該接口的實現類中為不同的聚合結構提供不同的遍歷方式,而客戶端可以一致性地操作該接口。
但是,由于容器與迭代器的關系太密切了,所以大多數語言在實現容器的時候都給提供了迭代器,并且這些語言提供容器和迭代器在絕大多數情況下就可以滿足我們的需要,所以現在需要我們自己去實踐迭代器的場景還是比較少的,一般來說只需要使用語言中已有的容器和迭代器就可以了。