Java の Iterator インターフェイスの利点は何ですか?
-
01-07-2019 - |
質問
Java Collections Framework がリンク リストのデータ構造を実装する方法について学びました。私が理解していることから、 Iterators
リストなどのデータ構造内の項目をたどる方法です。なぜこのインターフェイスが使用されるのでしょうか?なぜメソッドがあるのか hasNext()
, next()
そして remove()
データ構造の実装自体に直接コーディングされていませんか?
Java Web サイトから: リンクテキスト
パブリック インターフェイス Iterator<E>
コレクションのイテレーター。Iteratorは、Java Collections Frameworkの列挙の代わりになります。イテレーターは2つの方法で列挙とは異なります。
このインターフェイスは、Java Collections Frameworkのメンバーです。
- イテレーターを使用すると、発信者は、明確に定義されたセマンティクスを伴う反復中に、基礎となるコレクションから要素を削除できます。
- メソッド名が改善されました。
グーグルで調べてみましたが、明確な答えは見つからないようです。Sun がなぜそれらを使用することにしたのかを誰かが解明してもらえますか?デザインが良くなったからでしょうか?セキュリティの強化?良い OO の練習ですか?
ご協力をよろしくお願いいたします。ありがとう。
解決
なぜこのインターフェイスが使用されるのでしょうか?
それは、クライアント プログラマがあらゆる種類のコレクションを反復できるようにする基本的な操作をサポートしているためです (注:必ずしも Collection
の中に Object
センス)。
なぜメソッドが...データ構造の実装自体に直接コード化されていませんか?
それらは、プライベートとしてマークされているだけなので、アクセスしていじることはできません。すなわち:
- を実装またはサブクラス化できます。
Iterator
これにより、反復処理される実際のオブジェクトを変更することなく、標準的なオブジェクトでは実行できない処理が実行されます。 - トラバース可能なオブジェクトでは、インターフェイスをトラバーサル メソッド、特に高度に特殊化されたメソッドで混乱させる必要はありません。
- 配ることができます
Iterators
希望する数のクライアントにアクセスでき、各クライアントは独自の時間、独自の速度で移動できます。 - ジャワ
Iterators
特に java.util パッケージからのものは、それらをサポートするストレージがまだ変更されている場合に例外をスローします。Iterator
外。この例外により、Iterator
無効なオブジェクトを返している可能性があります。
単純なプログラムの場合、これはおそらくどれも価値があるとは思えません。ただし、それらを便利にする複雑さはすぐに思いつくでしょう。
他のヒント
あなたが尋ねる:「メソッド hasNext()、next()、remove() がデータ構造の実装自体に直接コード化されていないのはなぜですか?」
Java コレクション フレームワークは、コレクション自体に外部化されたものとして Iterator インターフェイスを定義することを選択します。通常、すべての Java コレクションは Iterable
Java プログラムが呼び出すインターフェイス iterator
ループ内で使用できるように独自のイテレータを作成します。他の人が指摘したように、Java 5 では、for-each ループを使用してイテレータを直接使用できます。
イテレータをそのコレクションに外部化すると、クライアントはコレクションを反復処理する方法を制御できるようになります。これが役立つユースケースの 1 つは、インターネット上のすべての Web ページなどの無制限のコレクションにインデックスを付ける場合です。
古典的な GoF 本では、内部イテレータと外部イテレータの対比が非常に明確に説明されています。
基本的な問題は、イテレータとイテレータを使用するクライアントのどちらが反復を制御するかを決定することです。クライアントが反復を制御する場合、その反復子は外部反復子と呼ばれ、反復子が制御する場合、その反復子は内部反復子と呼ばれます。外部イテレータを使用するクライアントは、トラバーサルを進めて、イテレータから次の要素を明示的に要求する必要があります。対照的に、クライアントは内部反復子に実行する操作を渡し、反復子はその操作をすべての要素に適用します。
外部イテレータは内部イテレータよりも柔軟です。たとえば、外部反復子を使用して 2 つのコレクションが等しいかどうかを比較するのは簡単ですが、内部反復子では事実上不可能です。しかしその一方で、内部反復子は反復ロジックを定義するため、より使いやすくなります。
内部イテレータがどのように動作するかの例については、Ruby のを参照してください。 Enumerable
API。次のような内部反復メソッドがあります。 each
. 。Ruby では、コードのブロック (つまり、クロージャ) を内部イテレータに追加して、コレクションが独自の反復を処理できるようにします。
コレクションをポインターから離しておくことは重要です。イテレータはコレクション内の特定の場所を指すため、コレクションの不可欠な部分ではありません。こうすることで、たとえば、同じコレクションに対して複数のイテレータを使用できます。
この分離の欠点は、反復処理の対象となるコレクションに加えられた変更を反復子が認識できないことです。そのため、コレクションの構造を変更して、反復子が「苦情」なしで作業を継続することを期待することはできません。
の使用 Iterator
インターフェイスを使用すると、そのメソッドを実装するクラスがイテレータとして機能できるようになります。Java におけるインターフェースの概念は、ある意味、クラスに特定の機能を提供するという契約上の義務を負うことです。 implements
インターフェースに必要な方法で動作します。有効なクラスであるためには契約上の義務を満たさなければならないため、そのクラスを参照する他のクラスは、 implements
したがって、クラスがこれらの特定の機能を備えていることを知ることで安心できます。
この例では、メソッドを実装するのではなく (hasNext(), next(), remove()
) の中に LinkedList
クラス自体、 LinkedList
クラスはそれを宣言します implements
の Iterator
インターフェイスなので、他の人もそれを認識できます。 LinkedList
イテレータとして使用できます。次に、 LinkedList
クラスはからメソッドを実装します Iterator
インターフェイス(など) hasNext()
)、イテレータのように機能します。
言い換えれば、インターフェイスの実装は、特定のクラスがそのクラスが主張するものであるために必要なものを備えていることを他の人に知らせるためのオブジェクト指向プログラミングの概念です。
この概念は、インターフェイスを実装するクラスによって実装される必要があるメソッドを持つことによって強制されます。これにより、他のクラスがそのクラスを実装するクラスを使用するようになります。 Iterator
インターフェイスには、イテレータが持つべきメソッドが実際にあることを示します。 hasNext()
.
また、Java には多重継承がないため、インターフェイスを使用してその機能をエミュレートできることにも注意してください。複数のインターフェイスを実装することにより、一部の機能を継承するサブクラスであるクラスを持つことができますが、インターフェイスを実装することで別の機能を「継承」することもできます。一例として、次のようなサブクラスが必要です。 LinkedList
というクラス ReversibleLinkedList
逆の順序で繰り返すことができるので、というインターフェイスを作成します。 ReverseIterator
そしてそれが提供することを強制します previous()
方法。以来、 LinkedList
すでに実装されています Iterator
, の場合、新しい可逆リストは両方を実装します。 Iterator
そして ReverseIterator
インターフェース。
インターフェイスの詳細については、次のサイトから読むことができます。 インターフェースとは何ですか? Sun の Java チュートリアルより。
インタレーターの複数のインスタンスを同時に使用できます。基礎となるデータのローカル カーソルとしてそれらにアプローチします。
ところで:具体的な実装よりもインターフェイスを優先すると、結合が緩みます。
イテレータの設計パターンを探します。ここにあります。 http://en.wikipedia.org/wiki/Iterator
データ構造ではないものを反復処理している可能性があるためです。サーバーから結果を取得するネットワーク化されたアプリケーションがあるとします。これらの結果の Iterator ラッパーを返すことができます。 それらをストリーミングします Iterator オブジェクトを受け入れる標準コードを通じて。
これは、優れた MVC 設計の重要な部分であると考えてください。データはモデルから取得する必要があります (つまり、データ構造)を何らかの方法で View に追加します。イテレータを仲介者として使用すると、モデルの実装が公開されることがなくなります。LinkedList をメモリ内に保持したり、復号化アルゴリズムから情報を引き出したり、JDBC 呼び出しをラップしたりすることができます。ビューは Iterator インターフェイスのみを考慮するため、ビューにとっては単純に問題ではありません。
イテレータの使用の長所と短所について論じた興味深い論文:
それはちょうど良い OO の実践だと思います。あらゆる種類のイテレータを処理するコードを作成でき、独自のデータ構造やイテレータ インターフェイスを実装する汎用クラスを作成する機会も提供します。背後にどのような実装があるのかを心配する必要はありません。
知らなかった場合は、M2C だけです。次のような状況ではイテレータ インターフェイスを直接使用することを避けることができます。 それぞれに ループで十分です。
最終的には、Iterator は多数のデータ構造に適用できるコントロールの抽象化をキャプチャするためです。もしあなたが圏論を理解しているなら、この論文を読んで驚かれるかもしれません。 イテレータパターンの本質.
最初の箇条書きにより、マルチスレッド (失敗した場合はシングルスレッド) アプリケーションが同時実行違反のためにコレクションをロックする必要がなくなるようです。たとえば、.NET では、IEnumerable をロックしたり継承したりメソッドをオーバーライドしたりすることなく、コレクション (またはリストまたは IEnumerable) を列挙し、変更することを同時に行うことはできません (例外が発生します)。
Iterator は、項目のコレクションを調べる一般的な方法を追加するだけです。優れた機能の 1 つは、反復処理しているリストから要素を削除できる i.remove() です。通常、リストから項目を削除しようとすると、奇妙な効果が発生したり、スローや例外が発生したりします。
インターフェイスは、それを実装するすべてのものに対する契約のようなものです。あなたは基本的に言っています..イテレータを実装するものはすべて、同じように動作するこれらのメソッドを持つことが保証されます。コード内で処理することだけを気にする場合は、これを使用して反復子の型を渡すこともできます。(リストの種類は気にしないかもしれません。これらすべてのメソッドを独立してコレクションに配置することもできますが、それらが同じように動作するか、同じ名前や署名を持つかどうかは保証されません。
イテレータは、Java で利用できる多くの設計パターンの 1 つです。デザイン パターンは、コード/構造の便利な構成要素、スタイル、使用法と考えることができます。
Iterator 設計パターンの詳細については、Iterator や他の多くの設計パターンについて説明しているこの Web サイトを参照してください。以下は Iterator のサイトからの抜粋です。 http://www.patterndepot.com/put/8/Behavioral.html
イテレーターは、デザインパターンの中で最もシンプルで最も頻繁に使用されるものの1つです。Iteratorパターンを使用すると、そのデータの内部表現の詳細を知ることなく、標準インターフェイスを使用してデータのリストまたはコレクションを移動できます。さらに、特別な処理を実行する特別なイテレーターを定義し、データ収集の指定された要素のみを返すこともできます。
イテレータは、あらゆる種類のコレクションに対して使用できます。これらを使用すると、基礎となる実装に関係なく、項目のコレクションに対するアルゴリズムを定義できます。これは、リスト、セット、文字列、ファイル、配列などを処理できることを意味します。
今から 10 年後、リストの実装をより良い実装に変更しても、アルゴリズムは引き続きそれに対してシームレスに実行されます。
イテレータは、Java でコレクションを扱う場合に便利です。
使用 それぞれに コレクション、配列、リストを反復処理するためのループ(Java1.5)。
java.util.Iterator インターフェースは Java Collections Framework で使用され、コレクションを反復処理しながらコレクションを変更できるようにします。コレクション全体をきれいに反復したいだけの場合は、代わりに for-each を使用しますが、Iterators の利点は次のような機能が得られることです。オプションのremove()操作、さらにadd()およびset()操作も提供するList Iteratorインターフェースに最適です。これらのインターフェイスの両方を使用すると、コレクションを反復処理し、同時に構造的に変更することができます。for-each を使用してコレクションを反復処理しているときにコレクションを変更しようとすると、ConcurrentModificationException がスローされます。これは通常、コレクションが予期せず変更されたためです。
ArrayList クラスを見てみましょう
ITRとlistitrと呼ばれる2つのプライベートクラス(内部クラス)があります
これらはそれぞれ Iterator インターフェイスと ListIterator インターフェイスを実装します。
パブリック クラス ArrayList....{ //それを囲むクラス
private class Itr implements Iterator<E> {
public E next() {
return ArrayList.this.get(index++); //rough, not exact
}
//we have to use ArrayList.this.get() so the compiler will
//know that we are referring to the methods in the
//enclosing ArrayList class
public void remove() {
ArrayList.this.remove(prevIndex);
}
//checks for...co mod of the list
final void checkForComodification() { //ListItr gets this method as well
if (ArrayList.this.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
private class ListItr extends Itr implements ListIterator<E> {
//methods inherted....
public void add(E e) {
ArrayList.this.add(cursor, e);
}
public void set(E e) {
ArrayList.this.set(cursor, e);
}
}
}
メソッドのiterator()とlistiterator()を呼び出すと、プライベートクラスのITRまたはlistItrの新しいインスタンスを返し、これらの内部クラスは囲いアレイリストクラス内にあるため、concurrentModificationExceptionをトリガーすることなくArrayListを自由に変更できます。 、ArrayListクラスのadd()またはremove()メソッドをset()add()またはremaid()メソッドを介して(conccurenty)同時に(conccurenty)変更しない限り。