java.util.Iterator
java.util.IteratorはArrayListなどのコレクションに対して繰り返し処理を行う為のインターフェイスです。
Iteratorを使うことで、様々な集合に対する繰り返し処理を共通のインターフェイスで実装できます。
ArrayListに対してiterator()メソッドを呼び出すと、戻り値としてIteratorインターフェイスの実装クラス(インスタンス)を取得できます(実装クラスがなにかは解りませんが、知る必要もありません)。
このIteratorは反復子とも呼ばれ、次の3つのメソッドが定義されています。
戻り値 | メソッド | 概要 |
---|---|---|
boolean | hasNext() | 繰り返し処理でさらに要素がある場合に true を返します。 |
Object | next() | 繰り返し処理で次の要素を返します。 |
void | remove() | 基になるコレクションから、反復子によって最後に返された要素を削除します (任意のオペレーション)。 |
この中でremove()メソッドは「任意のオペレーション」と定義されていますが、これはremove()メソッド自体は呼び出せますが、サポートしている場合とサポートしていない場合があることを示します。
反復子
反復子とは順次処理を行う時のカーソルのようなイメージです。
ある一覧の各要素に対し、先頭から順になんらかの処理を行うロジックをイメージして下さい。
恐らく、現在処理している箇所にカーソルを置いてから処理を行い、処理が終わったならばカーソルを1つ進めて繰り返すでしょう。
この時に重要な事は次の2点です。
- 次の要素があるか?
- 次の要素がある場合、その要素はなにか?
これはhasNext()メソッドとnext()メソッドがそれぞれ対応しており、カーソルに相当する概念が反復子です。
その反復子をJavaで表現すると、java.util.Iteratorインターフェイスとなります。
繰り返し処理
Iteratorを使った繰り返し処理はfor文を用いて次のように記述します。
ArrayList list = new ArrayList(); // 要素の追加 for(Iterator iter = list.iterator(); iter.hasNext(); ) { Object obj = iter.next(); // ArrayListの要素に対する処理 }
while文を用いて書くことも可能ですが、Iterator変数をwhile文の外に書かねばならない為、一般的にはfor文を用います。
行われている処理は単純です。
Iterator型変数iterを宣言します。
この時点では反復子の指す場所(カーソル)は0番目にあり、次の要素があるかが判定されます。
次の要素があった場合(iter.hasNext() == true)、ブロックの中の処理が実行されます。
ブロックの中ではnext()が呼ばれて1番目の要素が取得されます。
この時、反復子の指す場所(カーソル)は1番目へと移動します。
このような処理が最後の要素まで繰り返されます。
ArrayListの場合、Iteratorによる繰り返し処理と、size(),get()メソッドを用いたfor文による繰り返し処理と結果は変わりません。
ところが、Iteratorによる繰り返し処理はArrayListのような順序付の集合でなくとも同じ記述が可能です。
もし、繰り返したい要素の集合がArrayListではなく順序が定義されていない集合だったとしても、集合の要素数はある為にsize()メソッドはあるでしょう。
しかし、順番が定義されていないのですから、get(int index)メソッドは定義する事ができません。
ところが、順序が定義されていない集合であっても、全要素に対して1回づつ処理を行いたい場合はあると思います。
このような時、反復子であれば「次の要素があるか」と「次の要素はなにか」だけを意識すればいい為、順序は無視して反復処理を行えるようなクラス(すなわちIteratorのサブクラス)を作ることができます。
この時、順番が定義されていなくともIteratorを使った繰り返し処理は同じように記述できのです。
例えば、次のクラスはIteratorの実装クラスです。
package example05; import java.util.Iterator; public class HelloWorldIterator implements Iterator { private int counter = 0; public boolean hasNext() { if(counter < 2) { return true; } return false; } public Object next() { if(counter == 0){ counter++; return "Hello"; } else if(counter == 1) { counter++; return "World!"; } return null; } public void remove() { // 未定義 } }
学んでいない事も多い為に不完全な実装ですが、このクラスもIteratorとしての必要最低限の実装を行っています。
このHelloWorldIteratorクラスはIteratorインターフェイスを実装している為、ArrayListの時と同様の繰り返し処理を行えます。
for(Iterator iter = new HelloWorldIterator(); iter.hasNext(); ) { Object obj = iter.next(); // ArrayListの要素に対する処理 }
このようなIteratorを使った順次処理は、Iteratorパターンと呼ばれます。
順番に処理するだけであり順序に従って処理するとは限らないことは注意して下さい。
Iteratorを使う場合の注意
Iteratorを使った繰り返し処理は定型的に書くことができますが、next()メソッドが呼ばれることで反復子(カーソル)が進む事には十分に注意しなくてはなりません。
つまり、一回の処理で2回以上next()を呼び出したり、全く呼び出さなかった場合には予期せぬ挙動となってしまいます。
一般的に2回以上next()が呼び出されたりするコードは書くべきではありません。
Iteratorは原則として、全ての要素に対して1要素づつ順番に処理する場合に使用します。
許容できるのは、特定の条件でループを抜ける(break)程度でしょう。
[好ましくない反復処理]
for(Iterator iter = list.iterator(); i.hasNext(); ) { Object obj1 = iter.next(); // 奇数番目の処理 if(i.hasNext() == false) { break; } Object obj2 = iter.next(); // 偶数番目の処理 }
[1回のループでnext()は1回の方が綺麗なコードになる]
int counter = 0; for(Iterator iter = list.iterator(); i.hasNext(); ) { Object obj = iter.next(); if(counter % 2 == 1) { // 奇数番目の処理 } else { // 偶数番目の処理 } counter++; }