[Java] Iteratorのメモ


# 目的

- Iterator の使いどころの整理
- 類似の ListIterator についても簡単に
- ついでに古いコレクションフレームワークの Enumeration も少しだけ

# java.util.Iterator について

コレクションなどの要素を順番に処理する場合に使用します。
といっても、Java5.0 でサポートされた拡張for文があるため、Iteratorを明示的に使用することはありまり多くありませんが。Java8 ではストリーム関係が追加されて、さらに使われることが少なくなりそうです。

しかし場合によっては Iterator を明示的に使用しないといけないケースはあると思います。

## メソッド

簡単に。

- `Iterator#hasNext()` : 次のデータがある場合に真
- `Iterator#next()` : 次のデータを取得
- `Iterator#remove()` : 読み込んだデータを削除 (オプション操作)
- `Iterator#forEachRemaining()` : 各要素を順番に処理する(Java8で追加)

`remove()` はオプション操作なので、扱う `Iterator` によっては削除ができない場合があります（例えば `Collections.unmodifiableList()` は変更不能なリストを返すので、そこから取得した `Iterator` では削除ができません）。
ちなみに、`remove()` は Java8 からデフォルトメソッドになりました。
デフォルト実装は `UnsupportedOperationException` を投げるだけのものです。

また、Java8 で追加された `forEachRemaining()` もデフォルトメソッドです。

## 要素を削除する

明示的に `Iterator` を扱わないといけないケースは、要素を削除したい場合がほとんどだと思います。

不要な要素、あるいは処理済みになった要素を、コレクションから削除する場合には、以下のようにすると思います。

```Java
List<Integer> list = new LinkedList<>(Arrays.asList(0, 3, 1, 4, 1, 5, 9, 2, 6));

Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
	int num = iterator.next();
	if (num == 0) {
		// 0 は削除
		iterator.remove();
    }
}

System.out.println(list); // -> [3, 1, 4, 1, 5, 9, 2, 6]
```

`remove()` は前回 `next()` が返した要素を削除します。
そのため `next()` を呼び出していない場合や、既に `remove()` を呼び出している場合には、呼び出すことができません（例外が投げられます）。


# java.util.ListIterator について

`Iterator` よりも影の薄いものに `ListIterator` があります。
名前のとおり、リスト用に拡張されたイテレータです。

`Iterator` と違うのは、逆方向にも移動できることと、要素の追加や置き換えができることです。
また、`Iterator` は先頭からしか取得できませんが、 `ListIterator` はリストの任意の位置から取得することができます。

## メソッド

- `ListIterator#hasNext()` : 次のデータがある場合に真
- `ListIterator#next()` : 次のデータを取得
- `ListIterator#hasPrevious()` : 前のデータがある場合に真
- `ListIterator#previous()` : 前のデータを取得
- `ListIterator#remove()` : 読み込んだデータを削除 (オプション操作)
- `ListIterator#forEachRemaining()` : 各要素を順番に処理する(Java8で追加)
- `ListIterator#add()` : next() で返ってくる要素の手前にデータを追加 (オプション操作)
- `ListIterator#set()` : 読み込んだデータを置き換え (オプション操作)
- `ListIterator#nextIndex()` : `next()` で返ってくる要素のインデックス(末尾の場合には、リストのサイズ)
- `ListIterator#previousIndex()` : `previous()` で返ってくる要素のインデックス(先頭の場合には、`-1`)

## ListIterator の取得

`ListIterator` は基本的に `List#listIterator()` で取得します。

引数なしなら、先頭からの `ListIterator` を取得します。

引数ありなら、指定位置からの `ListIteraot` を取得します。

例えば、対象のリストが `target` だとします。

- 引数なしと同じ位置は `target.listIterator(0)`
- 先頭１つを飛ばす場合は `target.listIterator(1)`
- 一番最後からの場合は `target.listIterator(target.size())`

## 要素を追加する場合

リストの要素を追加したい場合には `add()` を使います。
例として、既にある要素を複数に分割するケースを示します。

```java
List<Integer> list = new LinkedList<>(Arrays.asList(0, 3, 1, 4, 1, 5, 9, 2, 6));

ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
	int num = iterator.next();
	if (num == 0) {
		// 0 は削除
		iterator.remove();

	} else if (num % 2 == 0) {
		// 2 で割り切れる場合には、分割
		iterator.remove();
		int sub = num / 2;
		iterator.add(sub); // ★
		iterator.add(sub); // ★
    }
}

System.out.println(list); // -> [3, 1, 2, 2, 1, 5, 9, 1, 1, 3, 3]
```

`add()` で追加した要素は `previous()` 方向に追加されます。
つまり、以下のようになります。

- 次に `next()` を呼び出しても取得できない
- 次に `previous()` を呼び出すと取得できる

先ほどは `remove()` した後に `add()` しましたが、単純に `add()` すると以下のようになります。

```java
List<Integer> list = new LinkedList<>(Arrays.asList(0, 3, 1, 4, 1, 5, 9, 2, 6));

ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
	int num = iterator.next();
	if (num == 0) {
		// 0 は削除
		iterator.remove();

	} else if (num % 2 == 0) {
		// 2 で割り切れる場合には、分割
		iterator.remove();
		int sub = num / 2;
		iterator.add(sub);
		iterator.add(sub);

	} else if (num == 1) {
		// 1 の後には 999 を追加
		iterator.add(999); // ★
	}
}

System.out.println(list); // -> [3, 1, 999, 2, 2, 1, 999, 5, 9, 1, 1, 3, 3]
```

## 要素を置換したい場合

リストの要素を置換したい場合には `set()` を使います。

```java
List<Integer> list = new LinkedList<>(Arrays.asList(0, 3, 1, 4, 1, 5, 9, 2, 6));

ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
	int num = iterator.next();
	if (num == 0) {
		// 0 は削除
		iterator.remove();

	} else if (num % 2 == 0) {
		// 2 で割り切れる場合には、分割
		iterator.remove();
		int sub = num / 2;
		iterator.add(sub);
		iterator.add(sub);

	} else if (num == 1) {
		// 1 の後には 999 を追加
		iterator.add(999);

	} else if (num == 5) {
		// 5 は 25 に置換
		iterator.set(25); // ★
	}
}

System.out.println(list); // -> [3, 1, 999, 2, 2, 1, 999, 25, 9, 1, 1, 3, 3]
```

`set()` は、前回 `next()` あるいは `previous()` が返したものを置換します。
そのため、以下のような状況では例外が発生します。

- `next()` あるいは `previous()` を呼び出す前
- `add()` あるいは `remove()` を呼び出した後

（ちなみに `remove()` も同様に前回 `next()` あるいは `previous()` が返したものを削除します）

## 逆方向にも進めたい場合

イテレータは順方向にしか進めませんが、 `ListIterator` は逆方向にも進めます。

以下の例は、要素を複数に分割した際に分割した要素についてもさらに処理をしたいケースを示します。

```java
List<Integer> list = new LinkedList<>(Arrays.asList(0, 3, 1, 4, 1, 5, 9, 2, 6));

ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
	int num = iterator.next();
	if (num == 0) {
		// 0 は削除
		iterator.remove();

	} else if (num % 2 == 0) {
		// 2 で割り切れる場合には、分割
        // さらに分割したものも処理する
		iterator.remove();
		int sub = num / 2;
		iterator.add(sub);
		iterator.add(sub);
        iterator.previous(); // ★
        iterator.previous(); // ★

	} else if (num == 1) {
		// 1 の後には 999 を追加
		iterator.add(999);

	} else if (num == 5) {
		// 5 は 25 に置換
		iterator.set(25);
	}
}

System.out.println(list); // -> [3, 1, 999, 1, 999, 1, 999, 1, 999, 1, 999, 1, 999, 25, 9, 1, 999, 1, 999, 3, 3]
```

単純に、リストの後ろから処理したいという場合もあるかもしれません。
その場合 `hasNext()` , `next()` の代わりに `hasPrevious()` , `previous()` を使うことで実現できます。

気をつけないといけないのは `add()` は `previous()` 方向に追加するので、次の `previous()` で結果が返ってきてしまうことです。

# java.util.Enumeration について

古いコレクションライブラリなので、基本的に使わない……といいたいのですが、古いライブラリでは直接 `Enumeration` を返すメソッドがあったりするので、たまに使わないといけないです。

Java9 で `asIterator()` というデフォルトメソッドが用意されたので、 `Iterator` 化も簡単になりました。

```java
  enumeration.asIterator().forEachRemaining(i -> foo(i));
```

また `Collections#list()` メソッドで、別の `List` に詰めなおすことができるので、拡張for文で扱いたい場合などはこれを使うと便利だと思います。
データ量が多い場合にはちょっと無駄な気がして不安になりますが。

```java
  for (var i : Collections.list(enumeration)) {
    foo(i);
  }
```

逆に、既存の `Collection` を `Enumeration` にしたいケースでは `Collections#enumeration()` が使えます。

```java
  @Override
  public Enumeration<String> getHeaderNames() {
    return Collections.enumeration(map.keySet());
  }
```


# その他

## 空の Iterator など

あまり使わないと思いますが、Java7 から空の Iterator などを取得するメソッドも用意されています。

- Collections#emptyIterator()
- Collections#emptyListIterator()
- Collections#emptyEnumeration()

## 直接 Iterator を返された場合

いくつかのライブラリでは、`Iterable#iterator()` 以外に、直接 `Iterator` を返すメソッドがあります。
あるいはメソッド名は `iterator()` なのに `Iterable` を実装していないとか、メソッド名が異なるなどのケースがあると思います。

そのような場合には(Java8以降なら)ラムダ式やメソッド参照で `Iterable` 化することが可能です。

```java
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(0, 3, 1, 4, 1, 5, 9, 2, 6));

// 逆順に処理する
for (int num : (Iterable<Integer>) list::descendingIterator) {
	System.out.println(num);
}
```

残念ながら、直接ラムダ式やメソッド参照を書けないので、キャストが必要です。

## Iterator を Stream にする

参考： [IteratorをStreamにする](http://qiita.com/yoshi389111/items/7cd7883d1d838e95a9fa)

## プリミティブ型のIterator

Java8から `java.util.PrimitiveIterator` というプリミティブ型用の Iterator インタフェースが追加されています。
実際には `PrimitiveIterator.OfInt` などのサブインターフェースを実装することになると思いますが。

通常の Iterator ではオートボクシングされてしまいますが、追加されているメソッドを使えばプリミティブ型のまま使用できます。
プリミティブ型の Iterator を作る場合には、このインタフェースを実装するとよいかもしれません。