Java Iterableインタフェース

Iterableインタフェースは、java.langパッケージに属し、コレクションで拡張for文(for-each文)が使えるようにするためのインタフェースです。java.langパッケージは基本的なクラスを提供するパッケージで、暗黙的にimportされるためimport文を明示する必要はありません。

ListやSetなどのルートインタフェースであるCollectionインタフェースがIterableを継承しているため、そのサブクラスとなるArrayListや、HashSet、ArrayDequeなどの実装クラスでも拡張for文を使うことができます。

Java Iterableを継承しているクラス

JavaのIterableインタフェースを継承しているクラスには、次のようなものがあります。

  • java.util.Collection: Collectionインタフェースは、JavaのCollection FrameworkのメンバーであるListやSetのルートインタフェースで、複数の要素を格納するためのインタフェースです。CollectionインタフェースはIterableインタフェースを継承しており、ListやSetなどがCollectionインタフェースを継承しています。

  • java.util.List: Listインタフェースは、順序付きの要素のコレクションを表します。ListインタフェースはCollectionインタフェースを継承しており、ArrayListやLinkedListなどのクラスがListインタフェースを実装しています。

  • java.util.Set: Setインタフェースは、順序を持たない、重複を許さない要素のコレクションを表します。SetインタフェースはCollectionインタフェースを継承しており、HashSetやTreeSetなどのクラスがSetインタフェースを実装しています。

  • java.util.Queue: Queueインタフェースは、要素を先入れ先出し順で管理するためのインタフェースです。QueueインタフェースはCollectionインタフェースを継承しており、PriorityQueueなどのクラスがQueueインタフェースを実装しています。

以上は一部です。Java Iterableインタフェースを継承しているすべてのサブクラスは拡張for文を使うことができます。

Java Iterableのメソッド

Iterableインタフェースは以下の3つのメソッドを持っています。

  • iterator(): 抽象メソッドのため実装はありません。
  • forEach(): デフォルトメソッドで処理が実装されています。
  • spliterator(): デフォルトメソッドで処理が実装されています。

iteratorメソッド

Iteratorオブジェクトを返すメソッドです。Iteratorとはコレクション内の要素を指す矢印のようなものです。Iteratorインタフェースについては後ほど説明します。抽象メソッドのためIterableインタフェースには実装がなく定義だけされています。Iterableを継承するサブクラスでは必ずiteratorメソッドをオーバーライドして実装する必要があります。

forEachメソッド

Iterableインタフェースのデフォルトメソッドとして実装されていて、コレクションの各要素をループし、Consumerを受け取り各要素に処理を実行することができます。forEachメソッドは、Java 8から追加された機能で、通常のfor文や、拡張for文、Iteratorを使用する代わりに、簡潔にコレクション内の要素を反復処理することができます。

forEachメソッドのシグネチャは、次のようになります。

void forEach(Consumer<? super T> action)

Consumerインタフェースは、引数を受け取り、何らかの処理を実行するためのインタフェースです。forEachメソッドに渡すアクションは、Consumerインタフェースを実装したラムダ式によって指定することができます。

例えば、List<Integer>のインスタンスを反復して、各要素を表示するには、次のように記述することができます。

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.*;
class Main {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        numbers.forEach(n -> System.out.println(n));

        // メソッド参照ではさらに簡潔に書けます
        numbers.forEach(System.out::println);
        System.out.println(numbers);
    }
}

Java Iteratorインタフェース

Iteratorはデザインパターンの1つで、コレクションの要素に対し順番にアクセスするために使用するインタフェースです。Iteratorは反復子ともいい、要素を指し示す矢印のようなものです。配列やMap, List, Setといったデータの集合体において内部の実装を意識することなく、集合を反復することができるようにします。

Java Iteratorインタフェースのメソッド

Iteratorインタフェースには、以下の4つのメソッドが定義されています。

  • boolean hasNext(): 次の要素が存在するかどうかを返します。
  • E next(): 次の要素を返します。
  • void remove(): 直前のnextで取得した要素を削除します。
  • void forEachRemaining(): 各要素に処理を実行します。

これらのメソッドを使用して、コレクションから要素を取得し、削除したり、次の要素が存在するかどうかを判定することができます。

import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Iterator;

class Main {
    public static void printIte(Iterator it) {
        while(it.hasNext()) {
            System.out.println(it.next());
        }
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("Java", "JavaScript", "Python", "PHP", "C++"));

        LinkedList<Character> linkedlist = new LinkedList<>(Arrays.asList('a', 'i', 'u', 'e', 'o'));

        Set<Integer> set = new HashSet<>(Arrays.asList(1,2,3,3,5));

        Map<String, String> map = new HashMap<>();
        map.put("apple", "リンゴ");
        map.put("orange", "みかん");
        map.put("grape", "ぶどう");

        printIte(list.iterator());
        printIte(linkedlist.iterator());
        printIte(set.iterator());
        printIte(map.entrySet().iterator());
    }
}

このように、集合に含まれる要素を順番に取得することができます。

Iteratorのremove

Iteratorを使うメリットは反復中に要素を削除するなどデータの構造を変更させるときです。例えばfor文や拡張for文を使って要素の削除をするとエラーになったり、想定される動きにならなかったりします。例えば以下のようにfor文でループ中に"J"で始まる要素を削除すると想定通りの動きにならないことがあります。

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("Java", "JavaScript", "Python", "PHP", "C++"));
        for(int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            // "J"で始まる要素を削除
            if(s.startsWith("J")) {
                list.remove(s); 
            }
        }
        System.out.println(list); // [JavaScript, Python, PHP, C++]
    }
}

拡張for文の場合はエラーが発生します。

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("Java", "JavaScript", "Python", "PHP", "C++"));
        for(String s: list) {
            // "j"で始まる要素を削除
            if(s.startsWith("J")) {
                list.remove(s); // ConcurrentModificationException が発生
            }
        }
        System.out.println(list); 
    }
}

Iteratorを使うと正しい挙動になります。

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("Java", "JavaScript", "Python", "PHP", "C++"));

        Iterator<String> it = list.iterator();
        while(it.hasNext()) {
            String s = it.next();
            // "j"で始まる要素を削除
            if(s.startsWith("J")) {
                it.remove(); 
            }
        }
        System.out.println(list); // [Python, PHP, C++]
    }
}

この記事を書いた人

著者の画像

Jeffry Alvarado

Ex-Facebook Engineer 大学ではコンピュータサイエンスを専攻し、在学中に複数のインターンシップを経験。コンピュータサイエンスが学習できるプラットフォームRecursionを創業し、CTOとしてカリキュラム作成、ソフトウェア開発を担当。


ツイート