読者です 読者をやめる 読者になる 読者になる

ITお絵かき修行

3歩歩いても忘れないために

Upgrade Java SE 7 to Java SE 8 OCP Programmer(1Z0-810)対策 第三章 ラムダ式を使用するコレクション(1)

第三章よりラムダ式を使用したstreamに対するフィルタリングを取り扱う。

■注意
正確な情報は下記リンクを参照してください。

■試験対策ページ
java.boot.by

■試験
Upgrade Java SE 7 to Java SE 8 OCP Programmer | Oracle Certification Exam

3.1. forEach()メソッドメソッド・チェーンを使用したコレクションの反復処理

繰り返し処理を制御するイテレータには一般的に2つの実装アプローチが存在する。

Activeな(外部)イテレータ
Activeなイテレータでは、イテレータの制御者が、次の要素へ処理が移ったことを伝えたり、各要素へアクセスした際に処理を行ったり等、制御者自身が生成した繰り返し処理を細かく制御できる。
Java1.0、1.1ではVectorとHashtableというコレクションクラスと、Iteratorデザインパターンを実装したEnumrationと呼ばれるクラスがあった。

Vector names = new Vector();
names.add("George");
names.add("Fred");
names.add("Ron");

Enumeration e = names.elements();
while (e.hasMoreElements()) {
    String name = (String) e.nextElement();
    System.out.println(name);
}

Java1.2ではIteratorデザインパターンIteratorというクラスに実装された。Iteratorでは未だに返却値のキャストが必要とされていた。Java1.2から1.4にかけて文字列の繰り返し処理はは下記のようなコードだった。

List names = new ArrayList();
names.add("George");
names.add("Fred");
names.add("Ron");

Iterator i = names.iterator();
while (i.hasNext()) {
    String name = (String) i.next();
    System.out.println(name);
}

Java5ではジェネリクスが導入され、Iteratorインタフェースクラスとforループの実装が改修された。

// Java SE 5 !!!
package java.lang;

import java.util.Iterator;

public interface Iterable<T> {
    public Iterator<T> iterator();
}
package java.util;

public interface Iterator<E> {
    public boolean hasNext();
    public E next();
    public void remove();
}

メモ:forループの内部では Iterator.hasNext() と Iterator.next()が呼ばれている。そのためActiveなイテレータとみなす事ができる。

List<String> names = new ArrayList<String>();
names.add("George");
names.add("Fred");
names.add("Ron");

for (String name : names) {
    System.out.println(name);
}


Passiveな(内部)イテレータ
Passiveなイテレータではイテレータ自身が繰り返し処理を制御する。イテレータの制御者は基本的に、コレクション内の要素に対するいくつかの操作をイテレータに行うよう指示します。Java8ではこのアプローチが利用可能である。

Java8ではjava.lang.IterableインタフェースクラスがforEachメソッドを持つ

package java.lang;

import java.util.Iterator;
import java.util.function.Consumer;

public interface Iterable<T extends Object> {

    public Iterator<T> iterator();

    public default void forEach(Consumer<? super T> cnsmr) {
        // ...
    }

    ...
}

java.util.Collectionインタフェースクラスはjava.lang.Iterableクラスを継承している。そのためjava.util.List もしくは java.util.Setインタフェースクラスの実装クラスは自動的にforEachメソッドを持っている。
java.util.Mapインタフェースクラスはjava.util.Collectionインタフェースクラスを継承していない。しかしJava8からは一貫性のためにforEachメソッドを持つ。
このメソッドはひとつのパラメータをとる関数型インタフェースである。そのため実体のパラメータを通すforEachメソッドラムダ式の候補となる。

List<String> names = new ArrayList<>();
names.add("George");
names.add("Fred");
names.add("Ron");

names.forEach(name -> System.out.println(name));

もしくはメソッド参照を使用する。

...
names.forEach(System.out::println);
...

上記のPassiveなイテレータと前セクションのActiveなイテレータとの差異を述べる。
Activeなイテレータのリスティング処理では、イテレーションのループ構造がイテレーションを制御し、ループを通過する度にループ内のオブジェクトはリストより回収され、文字列を出力する。
Passiveなイテレータのリスティング処理では、明確なループ処理は存在しない。単純に、リスト内におけるオブジェクトの処理(─今回の場合は単純な文字列の出力)をforEachメソッドにて呼び出す。
イテレーションの制御はforEachメソッドに依存する。

java.util.ListクラスにおけるforEachメソッドシグネチャ

package java.util;

public interface List<E extends Object> extends Collection<E> {

    /**
     * Performs the given action for each element of the Iterable until all elements have been
     * processed or the action throws an exception.
     */
    public default void forEach(Consumer<? super T> cnsmr) {
        ...
    }

    ...
}

java.util.SetインタフェースクラスにおけるforEachメソッドシグネチャ

package java.util;

public interface Set<E extends Object> extends Collection<E> {

    /**
     * Performs the given action for each element of the Iterable until all elements have been
     * processed or the action throws an exception.
     */
    public default void forEach(Consumer<? super T> cnsmr) {
        ...
    }

    ...
}


java.util.MapインタフェースクラスにおけるforEachメソッドシグネチャ

package java.util;

public interface Map<K extends Object, V extends Object> {

    /**
     * Performs the given action for each entry in this map until all entries have
     * been processed or the action throws an exception.
     */
    public default void forEach(BiConsumer<? super K, ? super V> bc) {
        ...
    }

    ...
}