前回から、Project Lambdaについて改めて取り上げています。
前回はラムダ式について解説を行いましたので、今回からStream APIについて取り上げていきます。
基本的には、Stream APIを使いこなす方法をメインに紹介していく予定です。しかし、Stream APIはかなりボリュームのあるAPIなので、今回はStream APIの基本的な部分を紹介していきます。このため、過去の連載と重なる部分もありますので、ご了承ください。
Stream APIとは
Stream APIは内部イテレータを実現させるAPIです。では、内部イテレータが何かというところから説明しましょう。
Javaでイテレータを実現するにはjava.util.Iteratorインタフェースか、拡張for文を使用してきました。例えば、リストの要素を拡張for文で標準出力に出力するには次のように記述します。
リスト1●外部イテレータの例
List<String> texts = ……;
for (String text: texts) {
System.out.println(text);
}
拡張for文ではイテレーションの処理を波括弧内に記述します。この例は単純な例なのでイテレーションする処理のみ記述しています。しかし、拡張for文では、波括弧内にbreak文やcontinue文などイテレータを制御するための式も記述できます。
このことは、イテレータの制御をコレクションの外側で行っていることを示します。このことから、拡張for文やIteratorインタフェースを使用したイテレータは外部イテレータと呼びます。
外部イテレータは、その特徴から、波括弧内にはイテレーションの処理とイテレータの制御が混在してしまいます。
これに対し、内部イテレータはイテレータの制御をコレクションの内側で行います。イテレーションしたい処理は関数やクロージャで記述して、コレクションに指定します。
例えば、Stream APIでは拡張for文的な処理を行うにはforEachメソッドを使用します。詳しくは後述しますが、リスト1をStream APIを使用した内部イテレータで書き直したのがリスト2です。
リスト2●内部イテレータの例
List<String> texts = ……;
texts.stream()
.forEach(text -> System.out.println(text));
forEachメソッドの引数に、ラムダ式でイテレーションの処理を記述します。イテレーションの制御を行うのはストリームで、リストの各要素はラムダ式の引数textに代入されます。
外部イテレータに比べると、内部イテレータはどのような処理をイテレーションするのかが明確になります。また、イテレーションの外側と内側では変数のスコープが変わるため、イテレーションの処理を独立にしやすいという特徴もあります。
その一方で、イテレータを制御できないため、外部イテレータに比べると記述性が劣ってしまいます。このため、外部イテレータでは記述できていた処理を、内部イテレータでは記述できないということもあります。