入門Iterator / Enumeration
環境
- JDK 21
Iteratorとは?
概要
Iterator は、リストなどのコレクションから全要素を1つずつ取得するためのインタフェースです。
主なメソッドは以下の2つです。
-
hasNext()- 次の要素があれば
trueを返します。
- 次の要素があれば
-
next()- 次の要素を返します。次の要素が無い場合は
NoSuchElementExceptionをスローします。
- 次の要素を返します。次の要素が無い場合は
ListやSetは Iterable インタフェースを継承しています。そしてIterableインタフェースには、Iteratorを取得できるiterator()メソッドが定義されています。
これらを踏まえて、サンプルコードを見てみましょう。
package com.example;
import java.util.Iterator;
import java.util.List;
public class ListIteratorMain {
public static void main(String[] args) {
// 適当なリストを定義
List<String> list = List.of("Alice", "Bob", "Charlie");
// リストのIteratorを取得
Iterator<String> iterator = list.iterator();
// 要素がある限り繰り返す
while (iterator.hasNext()) {
// 次の要素を取得
String name = iterator.next();
// 取得した要素を表示
System.out.println(name);
}
}
}
Alice
Bob
Charlie
package com.example;
import java.util.Iterator;
import java.util.Set;
public class SetIteratorMain {
public static void main(String[] args) {
// 適当なセットを定義
Set<String> set = Set.of("りんご", "バナナ", "すいか");
// セットのIteratorを取得
Iterator<String> iterator = set.iterator();
// 要素がある限り繰り返す
while (iterator.hasNext()) {
// 次の要素を取得(セットなので、定義時の順番で取得できるとは限らない)
String fruit = iterator.next();
// 取得した要素を表示
System.out.println(fruit);
}
}
}
バナナ
すいか
りんご
マップの場合は、キーと値のペアを保持するエントリーのセットを取得して、そのエントリーセットに対するIteratorを取得します。
package com.example;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapIteratorMain {
public static void main(String[] args) {
// 適当なマップを定義
Map<Integer, String> map = Map.of(
101, "Alice",
102, "Bob",
103, "Charlie"
);
// マップのエントリーセットを取得
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
// エントリーセットのIteratorを取得
Iterator<Map.Entry<Integer, String>> iterator = entrySet.iterator();
// 要素がある限り繰り返す
while (iterator.hasNext()) {
// 次の要素を取得(マップなので、定義時の順番で取得できるとは限らない)
Map.Entry<Integer, String> entry = iterator.next();
// 取得した要素のキーと値を表示
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
102 : Bob
103 : Charlie
101 : Alice
Iterator実装クラスはどこにある?
Iterator実装クラスは、各コレクションクラスの内部クラスとして定義されていることがほとんどです。
例えばArrayListクラスの場合、スーパークラスのAbstractListの内部クラスItrがIterator実装として使われます。詳しく知りたい方は、各クラスのソースコードを読んでみてください。
Iteratorと拡張for文
先ほど紹介したIteratorのサンプルコードのような書き方は、ほとんどしないと思います。リストやセットの全要素を1つずつ取得するには、拡張for文がよく利用されます。
package com.example;
import java.util.List;
public class ListForMain {
public static void main(String[] args) {
// 適当なリストを定義
List<String> list = List.of("Alice", "Bob", "Charlie");
// 拡張for文で、リストから1つずつ要素を取り出して、要素がある限り繰り返す
for (String name : list) {
// 取得した要素を表示
System.out.println(name);
}
}
}
実は、拡張for文の : の後ろにはIterableまたは配列のみ指定できます。ListやSetはIterableを継承しているので、このように拡張for文を利用できます。
Java言語仕様の14.14.2. The enhanced for statementに"The type of the Expression must be an array type (§10.1) or a subtype of the raw type Iterable, or a compile-time error occurs."と書かれています。
Iteratorとデザインパターン
Iteratorは、いわゆるGoFデザインパターンの一種です。デザインパターンについてもっと知りたい方は、以下の本を読んでみてください。
-
オブジェクト指向における再利用のためのデザインパターン(改訂版)
- いわゆるGoF本。サンプルコードがC++で書かれている。
-
増補改訂版Java言語で学ぶデザインパターン入門
- 結城浩さんによる本。サンプルコードがJavaで書かれているので、読むならこちらをおすすめします。
Enumerationとは?
概要
Enumeration もIteratorと同様に、リストなどのコレクションから全要素を1つずつ取得するためのインタフェースです。
メソッドも似ています。
-
hasMoreElements()- 次の要素があれば
trueを返します。
- 次の要素があれば
-
nextElement()- 次の要素を返します。次の要素が無い場合は
NoSuchElementExceptionをスローします。
- 次の要素を返します。次の要素が無い場合は
サーブレットのHttpServletRequestインタフェースには、すべてのリクエストヘッダー名をEnumeration形式で返すgetHeaderNames()メソッドが定義されています。これを利用してすべてのリクエストヘッダーを表示してみます。
HttpServletRequest request = ...;
// 全リクエストヘッダーの名前を保持するEnumerationを取得
Enumeration<String> headerNames = request.getHeaderNames();
// ヘッダー名がある限り繰り返す
while (headerNames.hasMoreElements()) {
// 次のヘッダー名を取得
String headerName = headerNames.nextElement();
// 取得したヘッダー名に対応する値を取得
String headerValue = request.getHeader(headerName);
// 取得したヘッダー名と値を表示
System.out.println(headerName + ": " + headerValue);
}
EnumerationからIteratorへの変換
実はEnumerationには、Iteratorに変換するasIterator()メソッドが定義されています。
HttpServletRequest request = ...;
// 全リクエストヘッダーの名前を保持するEnumerationを取得
Enumeration<String> headerNames = request.getHeaderNames();
// EnumerationをIteratorに変換
Iterator<String> iterator = headerNames.asIterator();
// ヘッダー名がある限り繰り返す
while (iterator.hasNext()) {
// 次のヘッダー名を取得
String headerName = iterator.next();
// 取得したヘッダー名に対応する値を取得
String headerValue = request.getHeader(headerName);
// 取得したヘッダー名と値を表示
System.out.println(headerName + ": " + headerValue);
}
なぜ似たインタフェースが2つあるのか
Javadocを確認したところ、EnumerationはJava 1.0から、IteratorはJava 1.2から存在します。つまりEnumerationの方が先に存在していました。
ここからは僕の予想なのですが、GoFデザインパターンで有名になったIteratorを、後から取り入れたのではないでしょうか。しかし既に存在していたEnumerationを削除する訳にもいかないため、似たインタフェースが2つ存在することになってしまったのだと思います。たぶん。
Discussion