「こんにちは、アミーゴ!」

「こんにちは、エリー!」

「今日はイテレーターについてお話したいと思います。」

「イテレータはコレクションとほぼ同時に発明されました。コレクションの主な目的は要素を格納することであり、イテレータの主な目的はこれらの要素を 1 つずつ取得することです。」

「一連の要素を取得することがそんなに難しいのですか?」

「まず、Set などの一部のコレクションの要素には、確立された順序がないか、順序が常に変化します。」

「第二に、一部のデータ構造は、オブジェクトを異なるグループ、リストなどに非常に複雑な方法で格納することがあります。言い換えれば、すべての要素を順番に渡すのは簡単な作業ではありません。」

「第三に、コレクションは変更される傾向があります。コレクションの内容全体を表示することにしたとします。しかし、出力の途中で JVM が別のスレッドに切り替わり、コレクションの要素の半分が置き換えられます。そのため、出力の代わりに、次のような結果が得られます。誰が何を知っていますか?」

"ふーむ..."

「しかし! これらはまさにイテレータが解決できる種類の問題です。イテレータはコレクション内の特別なオブジェクトであり、一方ではすべてのプライベート データにアクセスでき、その内部構造を知っていますが、他方では、パブリック Iterator インターフェイスを実装しており、誰もがその操作方法を知ることができます

「一部のイテレータには、イテレータの作成時にコレクションのすべての要素がコピーされる内部配列があります。これにより、コレクションに対する後続の変更が要素の数や順序に影響を与えなくなります。」

「 for eachを使用しているときに、この問題に遭遇したことがあると思います。コレクションをループし、コレクションから要素を削除することを同時に行うことはできません。これはすべて、まさにイテレータの動作方法によるものです。」

「同時実行ライブラリに追加された新しいコレクションでは、この問題を解決するためにイテレータが作り直されました。」

「イテレーターがどのように機能するかを思い出させてください。」

「Java には特別な Iterator インターフェイスがあります。そのメソッドは次のとおりです。」

Iterator<E> インターフェイスのメソッド 説明
boolean hasNext() 他に要素があるかどうかを確認します
E next() 現在の要素を返し、次の要素に移動します。
void remove() 現在の要素を削除します

「イテレータを使用すると、コレクションのすべての要素を連続して取得できます。イテレータは、InputStream のようなものと考えるのがより論理的です。イテレータにはすべてのデータが含まれていますが、そのタスクはそれを順番に出力することです。」

「   next () メソッドは、コレクション内の次の要素を返します。」

hasNext () メソッドは、さらに要素があるかどうかを確認するために使用されます。」

「そして、remove () は現在の要素を削除します。」

"質問は?"

「なぜメソッドにはそのような奇妙な名前が付いているのでしょうか? なぜ isEmpty() と getNextElement() ではないのでしょうか?」

「そのほうが理にかなっていませんか?」

「そのほうが理にかなっていますが、名前はイテレータが以前に登場した C++ 言語に由来しています。」

「なるほど。続けましょう。」

「反復子に加えて、反復子をサポートするすべてのコレクションによって実装される必要がある Iterable インターフェイスもあります。これには 1 つのメソッドがあります。」

Iterable<T> インターフェイスのメソッド 説明
Iterator<T>iterator() イテレータオブジェクトを返します

「任意のコレクションでこのメソッドを使用すると、反復子オブジェクトを取得してその要素をたどることができます。 TreeSet内のすべての要素をたどってみましょう。」

TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}

「このようなイテレータの使用はあまり便利ではありません。余分で明白なコードが多すぎます。Java にfor-eachループが登場すると、状況はより簡単になりました。」

「このコードはよりコンパクトで読みやすくなりました。」

TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}
TreeSet<String> set = new TreeSet<String>();

for(String item : set)
{
 System.out.println(item);
}

「これは同じコードです。どちらの場合もイテレータが使用されています。」

「それは、その使用がfor-eachループ内に隠されているというだけです。右側のコードには赤いテキストがまったくないことに注意してください。イテレータの使用は完全に隠されています。」

for-eachループは、反復子をサポートするオブジェクトに使用できます。言い換えれば、独自のクラスを作成し、それにiterator () メソッドを追加し、そのオブジェクトをfor-each構造で使用できます。」

「わあ! もちろん、独自のコレクションやイテレータを書きたいわけではありませんが、それでもその可能性は魅力的です。メモしておきます。」

さらに、独自のインターフェイスを持つ別の人気のあるタイプのイテレータもあります。私はリストの反復子、つまりListIteratorについて話しています。

「実装に関係なく、リストは要素の順序を維持するため、イテレータを介したリストの操作が少し便利になります。」

" ListIterator <E> インターフェイスのメソッドは次のとおりです。"

方法 説明
boolean hasNext() この先にさらに要素があるかどうかを確認します。
E next() 次の要素を返します。
int nextIndex() 次の要素のインデックスを返します
void set(E e) 現在の要素の値を変更します
boolean hasPrevious() 背後に要素があるかどうかを確認します。
E previous() 前の要素を返します
int previousIndex() 前の要素のインデックスを返します
void remove() 現在の要素を削除します
void add(E e) リストの最後に要素を追加します。

「言い換えれば、ここでは前進と後進の両方が可能です。他にも小さな機能がいくつかあります。」

「へー、それは面白いですね。どこで使われているんですか?」

「リンクされたリストを前後に移動したいとします。get 操作はかなり遅くなりますが、next() 操作は非常に高速になります。」

「うーん、納得しました。心に留めておきます。」

「ありがとう、エリー!」