23種設計模式 迭代器設計模式
使用迭代器模式,我們可以自己定義介面,但是在JDK中已經有定義好的迭代器介面,可以滿足大部分的使用場景:
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());
}
}
一般情況,我們只需要實現hasNext和next方法就可以了。
接下來我們就下一個簡單的例子:
定義自己的集合MyCollection,通過迭代器iterator,按照正序返回集合中的物件。
首先,定義我們的MyCollection類:
/**
* Created by li.zhipeng on 2017/9/21.
*
* 自定義集合類
*/
class MyCollection<T>{
private List<T> list = new ArrayList<>();
public void add(T item){
list.add(item);
}
public void remove(T item){
list.remove(item);
}
public Iterator<T> iterator(){
return new MyIterator(list);
}
/**
* 自定義迭代器
* */
private class MyIterator implements Iterator<T>{
private List<T> dataSource;
/**
* 當前位置的指標
* */
private int curPos;
public MyIterator(List<T> data){
this.dataSource = data;
}
@Override
public boolean hasNext() {
return curPos < dataSource.size();
}
@Override
public T next() {
T item = dataSource.get(curPos);
curPos ++;
return item;
}
}
}
為了簡單方便,我在MyCollection內部使用了ArrayList,然後封裝了兩個方法,新增和刪除ArrayList中的元素,iterator()方法返回我們自定義的迭代器,這裡還使用了泛型,規定了新增元素的型別,MyCollection的泛型肯定是要和迭代器是一樣的,這裡就不多說了。
請注意這裡的迭代器最好不要公用,儘量返回一個新的迭代器物件,因為如果多執行緒同時遍歷迭代器,這裡可能會影響到迭代器的指標,所以我每次都是建立一個新的迭代器。如果你更加關心多執行緒的併發操作,這裡最好每次建立Iterator的時候,應該加入同步鎖,傳入List的副本,提高多執行緒併發的安全性。
然後在MyCollection中定義了內部迭代器MyIterator,有一個私有屬性curPos,記錄遍歷的位置指標。
使用private修飾迭代器的原因
之前說過Iterator需要隱藏集合遍歷時獲取元素的規則,並且這個Iterator也只為我們當前的MyCollection服務,所以公開這個類是一個不明智的選擇,我這裡選擇隱藏這個類,只能通過MyCollection的iterator方法來獲取。
大部分的迭代器模式開發中,往往都是隱藏自定義迭代器。
定義完畢,測試一下我們的MyCollection:
public void test(){
MyCollection<Student> collection = new MyCollection<>();
collection.add(new Student("1111"));
collection.add(new Student("2222"));
Iterator<Student> iterator = collection.iterator();
while (iterator.hasNext()){
Student student = iterator.next();
System.out.println(student.getName());
}
}
當我還沉迷於自己的才華無法自拔的時候,產品君說了:我們現在要改成倒序遍歷了,你抓緊時間弄一下,明天我們要上線。
如果我之前使用的是for迴圈,那麼我就需要根據功能邏輯,找到遍歷的程式碼,然後一處一處的修改,如果有個100使用的地方,我覺得今晚是沒法睡了。
但是機智如我,我早就看穿了產品君騷動的內心,早早的使用了迭代器模式,我只需要對MyIterator做一點小調整:
/**
* 自定義迭代器
* */
private class MyIterator implements Iterator<T>{
private List<T> dataSource;
/**
* 當前位置的指標
* */
private int curPos;
public MyIterator(List<T> data){
this.dataSource = data;
// 把遍歷開始的指標,放到最後一個
curPos = data.size() - 1;
}
@Override
public boolean hasNext() {
// 修改是否遍歷的結束條件,這裡需要大於0
// return curPos < dataSource.size();
return curPos >= 0;
}
@Override
public T next() {
T item = dataSource.get(curPos);
// 這裡要把位置--
// curPos ++;
curPos--;
return item;
}
}