1はじめに
実際のアプリケーションでは、要素の内部条件を気にせずに、集約オブジェクト内のすべての要素にアクセスしようとします。たとえば、バスの車掌は、バスに乗る人が男性、女性、高齢者、子供を問わず、バスに乗る限りチケットを購入する必要があります。これはイテレータパターンに対応します。
2定義
イテレータパターン:集約オブジェクトの内部表現を公開せずに、集約オブジェクト内の一連のデータに順次アクセスするためのオブジェクトを提供します。
3構造と実装
イテレータパターンは、アグリゲートオブジェクトのトラバーサル動作を分離し、イテレータクラスに抽象化することで実装されます。その目的は、アグリゲートオブジェクトの内部構造を公開せずに、外部コードがアグリゲートの内部データに透過的にアクセスできるようにすることです。
イテレータパターンには、主に次の役割が含まれます。
- 抽象集約ロール:集約オブジェクトの保管、追加、削除、およびイテレーター・オブジェクトの作成のためのインターフェースを定義します。
- 具象集約ロール:具象イテレーターのインスタンスを返す抽象集合クラスを実装します。
- 抽象イテレーター(イテレーター)の役割:通常、hasNext()、first()、next()、およびその他のメソッドを含む、集約要素にアクセスしてトラバースするためのインターフェースを定義します。
- 具体的なイテレーターの役割:抽象イテレーター・インターフェースで定義されたメソッドを実装し、集約されたオブジェクトのトラバーサルを完了し、トラバーサルの現在の位置を記録します。
構造図を次の図に示します(写真は参考文献2からのものです)。
4長所と短所
4.1利点
- 内部表現を公開せずに、集約オブジェクトのコンテンツにアクセスします。
- トラバーサルタスクはイテレータによって実行されます。これにより、集約クラスが単純化されます。
- さまざまな方法でアグリゲートのトラバースをサポートし、イテレータをサブクラス化して新しいトラバーサルをサポートすることもできます。
- 元のコードを変更せずに、新しい集約クラスとイテレータクラスを追加すると非常に便利です。
- 優れたカプセル化。さまざまな集約構造をトラバースするための統一されたインターフェイスを提供します。
4.2デメリット
- クラスの数が増えると、システムがある程度複雑になります。
5アプリケーションシナリオ
- 集約されたオブジェクトをトラバースする複数の方法を提供する必要がある場合。
- さまざまな集約構造をトラバースするための統一されたインターフェースを提供する必要がある場合。
- 内部詳細の表現を公開せずに集約オブジェクトのコンテンツにアクセスする場合。
6コードの説明
Javaでは、Collection、List、Set、Mapなどすべてにイテレータが含まれています。次に、JavaでのListのイテレータ実装を見てみましょう。
6.1イテレータインターフェイスイテレータ
public interface Iterator<E> {
//获取下一个元素,第一次调用给出第一项,第二次给出第二项,。。。
E next();
//是否存在下一个,存在true,不存在false
boolean hasNext();
//从底层集合中删除迭代器返回的最后一个元素,就是next()返回的集合中的元素
default void remove() {
throw new UnsupportedOperationException("remove");
}
//对每个剩余的元素进行一定的操作,Consumer是函数式接口
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
インターフェイスのデフォルト関数は、Java8の新しく導入された機能です。デフォルトの変更されたメソッドは、インターフェイスでのみ使用できます。インターフェイスでデフォルトでマークされたメソッドは一般的なメソッドであり、メソッド本体を直接記述できます。
6.2ArrayListでのイテレータの実装
Iteratorインスタンスを取得します
public Iterator<E> iterator() {
return new Itr();
}
プライベートクラスItr
private class Itr implements Iterator<E> {
//下一个元素返回的索引
int cursor;
//最后一个元素的索引,没有为-1,获取实例Iterator时,为-1
int lastRet = -1;
// expectedModCount 预期集合元素被修改次数 modCount 集合被修改的数量
int expectedModCount = modCount;
Itr() {}
/**
* 判断元素
* @return 是否还有元素
*/
public boolean hasNext() {
return cursor != size;
}
//返回下一个元素
@SuppressWarnings("unchecked")
public E next() {
// 关键一步,调用checkForComodification()会去校验expectedModCount 和modCount是否相等,不等抛异常
//集合元素被修改一次,modCount自加一次
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//修改 让预期修改次数与实际修改次数相等
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList.this.remove(lastRet)
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
6.3主な機能テスト
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i + 1);
}
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
iterator.remove();
}
}
6.4デバッグテスト結果
1.リストに要素を追加した後、Iteratorインスタンスが取得されます。cursorの値とlastRetの値に注意してください。
2.要素を削除した後、イテレータが変更されます。
3.最終出力結果
6.5まとめ
このセクションでは、ArrayListでのイテレーターの実装を分析して、イテレーターパターンの設計とアプリケーションを説明します。これは私たちの実際のプログラミングの習慣と一致しており、日常の開発では、イテレータを自分で作成することはほとんどありません。自分で実装したデータ構造に対応するイテレータをカスタマイズする必要がない限り、オープンソースフレームワークが提供するAPIで十分です。
7引用
1.「Dahuaデザインパターン」
3.イテレータを使用してコレクションをトラバースする場合、コレクションを変更できないという引数
8ソースコード
説明はJavaのArrayListのソースコードであるため、このデザインパターンのソースコードはありません。