Java 8ストリームAPI:宣言的データ処理の新しいアプローチ
ストリームはJava 8で導入された新しい抽象レイヤーです。ストリームを使用すると、SQL文のように宣言的な方法でデータを処理できます。例えば、以下のSQL文を考えてみてください。
SELECT max(salary), employee_id, employee_name FROM Employee
このSQL式は、開発者側で計算を行うことなく、最高給与の従業員詳細を自動的に返します。Javaのコレクションフレームワークを使うと、開発者はループを使い繰り返しチェックを行う必要があります。もう一つの懸念は効率性です。マルチコアプロセッサが容易に利用可能になりましたが、Java開発者は並列処理コードを書く必要があり、それはかなりエラーを起こしやすいものです。
このような問題を解決するため、Java 8ではストリームの概念が導入され、開発者が宣言的にデータを処理し、マルチコアアーキテクチャを活用できるようになりました。そのために特別なコードを書く必要はありません。
ストリームとは?
ストリームは、集約操作をサポートするソースからのオブジェクトシーケンスを表します。主な特徴:
- 要素のシーケンス - 特定型の要素セットを順次的に提供。オンデマンドで要素を取得/計算。要素を格納しない
- ソース - コレクション、配列、I/Oリソースを入力ソースとする
- 集約操作 - filter、map、limit、reduce、find、match等をサポート
- パイプライニング - ほとんどのストリーム操作はストリーム自体を返し、結果をパイプライン可能。これらは中間操作と呼ばれ、入力を受け取り処理し、ターゲットに出力する。
collect()は通常パイプライン末尾にある終端操作 - 自動反復 - ストリーム操作はソース要素に対して内部的に反復処理を行う。コレクションでは明示的な反復が必要
ストリームの生成
Java 8ではCollectionインターフェースにストリーム生成の2メソッドが追加されました。
stream()- コレクションをソースとする順次ストリームを返すparallelStream()- コレクションをソースとする並列ストリームを返す
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
主要操作
forEach
ストリームの各要素を反復処理する新しいメソッドforEachを提供。
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
map
各要素を対応する結果にマッピングするmapメソッド。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); List<Integer> squaresList = numbers.stream().map(i -> i*i).distinct().collect(Collectors.toList());
filter
基準に基づいて要素を除外するfilterメソッド。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
int count = strings.stream().filter(string -> string.isEmpty()).count();
limit
ストリームサイズを縮小するlimitメソッド。
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
sorted
ストリームをソートするsortedメソッド。
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
並列処理
parallelStreamは並列処理用のstream代替手段。順次/並列ストリームの切り替えが容易。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
Collectors
ストリーム要素の処理結果を結合するために使用。リストや文字列を返せる。
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
Statistics
ストリーム処理時にすべての統計を計算する統計コレクターが導入。
List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt(x -> x).summaryStatistics();
System.out.println("Highest: " + stats.getMax());
System.out.println("Lowest: " + stats.getMin());
System.out.println("Sum: " + stats.getSum());
System.out.println("Average: " + stats.getAverage());
包括的なストリーム例(Java 7 vs Java 8比較)
空文字列カウント、長さ3文字列カウント、空文字除去、カンマ結合、平方数リスト、最大/最小/合計/平均、乱数生成などをJava 7(ループ)とJava 8(ストリーム)で比較実装した完全なサンプルコードにより、ストリームの宣言的・簡潔な記述と並列処理の容易さを実演。
Java
- Java演算子をマスター:種類、構文、実践例
- Javaコメント:種類、使い方、ベストプラクティス
- Java拡張forループをマスター:構文、例、ベストプラクティス
- Java文字列をマスター:生成、メソッド、ベストプラクティス
- Javaインターフェース完全ガイド:概念、実装、ベストプラクティス
- Java無名インナークラス完全ガイド:定義、構文、実践例
- Javaのtry-with-resourcesをマスターする:自動リソース管理の仕組みと活用法
- Javaアノテーション完全ガイド:種類、配置場所、実践例
- Javaアサーション完全ガイド:バグ早期発見とコード信頼性の向上
- Java 乱数生成:Random クラスと Math.random 実践ガイド
- Java 9ストリームAPI拡張:takeWhile、dropWhile、iterate、ofNullable