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) { ... } ... }