Java Language
イテレータとイテレータ
サーチ…
前書き
java.util.Iterator
は、Iteratorデザインパターンを実装するオブジェクトの標準Java SEインターフェイスです。 java.lang.Iterable
インタフェースは、イテレータを提供できるオブジェクト用です。
備考
Java配列はIterableを実装しませんが、 for
-eachループを使用して配列全体を反復することは可能です。反復は、バックグラウンドでアクセスできないインデックスを使用してJVMによって行われます。
forループでIterableを使う
Iterable<>
インタフェースを実装するクラスは、 for
ループで使用できます。これは、オブジェクトからイテレータを取得し、それを使ってすべての要素を連続的に取得するための構文砂糖です。コードをより明確にし、書込みを速くし、エラーを起こしにくくします。
public class UsingIterable {
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1,2,3,4,5,6,7);
// List extends Collection, Collection extends Iterable
Iterable<Integer> iterable = intList;
// foreach-like loop
for (Integer i: iterable) {
System.out.println(i);
}
// pre java 5 way of iterating loops
for(Iterator<Integer> i = iterable.iterator(); i.hasNext(); ) {
Integer item = i.next();
System.out.println(item);
}
}
}
生のイテレータを使用する
foreachループ(または "extended for loop")を使用するのは簡単ですが、イテレータを直接使用すると有益なことがあります。たとえば、コンマ区切り値の束を出力したいが、最後の項目にコンマをつけたくない場合は、次のようにします。
List<String> yourData = //...
Iterator<String> iterator = yourData.iterator();
while (iterator.hasNext()){
// next() "moves" the iterator to the next entry and returns it's value.
String entry = iterator.next();
System.out.print(entry);
if (iterator.hasNext()){
// If the iterator has another element after the current one:
System.out.print(",");
}
}
isLastEntry
変数を使用するか、ループインデックスを使用して計算するよりもはるかに簡単で分かりやすくなります。
独自のIterableを作成する。
どのインタフェースでも独自のIterableを作成するには、インタフェースに抽象メソッドを実装するだけです。 Iterable
にはiterator()
と呼ばれるものが1つしかありません。しかし、そのリターン型Iterator
はそれ自体、3つの抽象メソッドを持つインタフェースです。コレクションに関連付けられたイテレータを返すことも、独自のカスタム実装を作成することもできます。
public static class Alphabet implements Iterable<Character> {
@Override
public Iterator<Character> iterator() {
return new Iterator<Character>() {
char letter = 'a';
@Override
public boolean hasNext() {
return letter <= 'z';
}
@Override
public Character next() {
return letter++;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Doesn't make sense to remove a letter");
}
};
}
}
使用するには:
public static void main(String[] args) {
for(char c : new Alphabet()) {
System.out.println("c = " + c);
}
}
新しいIterator
は、最初のアイテムを指し示す状態でなければなりませんIterator
への各呼び出しは、次の状態を指すように状態を更新します。 hasNext()
はイテレータが最後にあるかどうかをチェックします。イテレータが変更可能なコレクションに接続されている場合、イテレータのオプションのremove()
メソッドが実装され、現在のコレクションからポイントされているアイテムを削除できます。
イテレータを使用した要素の削除
Iterator.remove()
メソッドは、 Iterator.next()
への以前の呼び出しによって返された要素を削除するオプションのメソッドです。たとえば、次のコードは文字列のリストを作成し、すべての空の文字列を削除します。
List<String> names = new ArrayList<>();
names.add("name 1");
names.add("name 2");
names.add("");
names.add("name 3");
names.add("");
System.out.println("Old Size : " + names.size());
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String el = it.next();
if (el.equals("")) {
it.remove();
}
}
System.out.println("New Size : " + names.size());
出力:
Old Size : 5
New Size : 3
上のコードは、典型的なコレクションを反復しながら要素を削除する安全な方法です。代わりに、次のようにコレクションから要素を削除しようとします。
for (String el: names) {
if (el.equals("")) {
names.remove(el); // WRONG!
}
}
イテレータにフェイル・ファスト・イテレータ・セマンティクスを提供する典型的なコレクション( ArrayList
など)は、 ConcurrentModificationException
をスローします。
remove()
メソッドは、 next()
呼び出しの後にのみ呼び出されます(一度)。 next()
呼び出す前に呼び出された場合、またはnext()
呼び出しの後に2回呼び出された場合、 remove()
呼び出しはIllegalStateException
をスローします。
remove
操作はオプションの操作として説明されています 。つまり、すべてのイテレータで許可されるわけではありません。サポートされていない例には、不変なコレクションのイテレーター、コレクションの読み取り専用のビュー、固定サイズのコレクションなどがあります。イテレータが削除をサポートしていないときにremove()
が呼び出されると、 UnsupportedOperationException
がスローされます。