LoginSignup
3
1

More than 3 years have passed since last update.

【Java】Iteratorはどういう時に使う必要があるのか

Last updated at Posted at 2021-04-30

3通りの方法で同じ処理を確認

配列内の値をぐるぐる確認して一致するものがあったら、、、というパターン。
例として適当に以下のサンプルコードを用意した。今回はData型の配列のidが一致した要素の時はフラグを立てるというもの。比較するときに取り上げるのは「中略」以降の内容のみ。

①普通のfor文を扱った場合

public class Data {
    public int id = 1000;
    public int date = 20210430;
    public String name = "name";

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    //他、dateとnameも同様の処理があり、setメソッドはどこかで使われているとする
}

public class Main {
    private static final int DEFAULT_ID = 1000;
    private boolean isDefault = false;
    private List<Data> dataList = new List<>();

    //~中略~

    for (int i = 0; i < dataList.size(); i++) {
        Data data = dataList.get(i);

        if (data.getId == DEFAULT_ID) {
            isDefault = true;
        }
    }
}

これが最も初歩的に学ぶfor文の扱い方かと思われる。

②拡張for文を扱った場合

for (Data data : dataList) {
    if (data.getId == DEFAULT_ID) {
        isDefault = true;
    }
}

dataList配列にあるData型の要素を順に1つずつ取り出してループする方法である。そして次がfor文を使わずにやる方法。

③iteratorを扱った場合

Iterator<Data> ite = dataList.iterator();
while (ite.hasNext()) {
    Data data = it.next();

    if (data.getId == DEFAULT_ID) {
        isDefault = true;
    }
}

iteratorを扱ってもできるが、ソースを比べても明らかに②拡張for文が一番シンプルでいい気がする。では③のiteratorを使わないといけない場面はどういう時なのだろうか気になった。
関わっていた案件で、iteratorが使われていて「拡張for文でもよくない?」と思ってローカルで修正してみたところ、ConcurrentModificationExceptionの例外が発生してしまった。調べてみると

この例外は、オブジェクトの並行変更を検出したメソッドによって、そのような変更が許可されていない場合にスローされます。

とあるがよくわからなく、iteratorについて手元にあった参考書を読んでみると、

たとえばループをしながら、要素を削除するには、イテレーターを利用します。

例外が発生したケースでは、上で挙げたサンプルとは違って一致した時はその要素を配列から削除する実装だった。まさにこれに該当することがわかった。

Iteratorを利用しなければならないケース

1. ループの中で要素を削除する

以下の実装だとNG

for (Data data : dataList) {
    if (data.getId == DEFAULT_ID) {
        dataList.remove(data);  //ConcurrentModificationException発生
    }
}



これをIteratorを扱って書き直すと例外が発生しない。

Iterator<Data> ite = dataList.iterator();
while (ite.hasNext()) {
    Data data = it.next();

    if (data.getId == DEFAULT_ID) {
        ite.remove();
    }
}

ちなみに拡張for文はNGだったが、①の普通のfor文なら試した限りだと例外が発生しなかったのでループ中に要素を削除したいなら普通のfor文は使える様子。

2. 配列を逆順に読み込む

ListIterator<Data> ite = dataList.listIterator(data.size());
while (ite.hasPrevious()) {
    if (data.getId == DEFAULT_ID) {
        ite.remove();
    }
}

for文でやる時は要素番号を工夫すればいけるだろうが、こっちの方が正確かつ簡単なのでこういうケースはIteratorを使っていくのが良い。

参考文献

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1