Stream API をざっくりイメージする
まず感覚から先に。
Stream API は、
「配列やコレクションなど“データの列”に対して、
“何をしたいか”を宣言的に書けるパイプライン」
です。
for 文で
- フィルタする(条件に合うものだけ残す)
- 値を変換する
- 合計を出す/集計する
という処理をゴリゴリ書いていたところを、filter、map、sum などの“つなげ書き”で表現できます。
イメージとしては
「データの流れ(ストリーム)に対して
“こう流して、こう加工して、最後にこう集計”と指示する」
感じです。
最初の一歩:for 文で書いていた処理を Stream にしてみる
例題:リストから偶数だけ抜き出して合計する
まずは従来の for 文で書いてみます。
import java.util.*;
public class ForLoopExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = 0;
for (int n : list) {
if (n % 2 == 0) { // 偶数だけ
sum += n; // 合計
}
}
System.out.println(sum); // 12
}
}
Javaこれを Stream API で書くと、こうなります。
import java.util.*;
public class StreamExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = list.stream() // ストリームを作る
.filter(n -> n % 2 == 0) // 偶数だけに絞る
.mapToInt(n -> n) // int ストリームに変換
.sum(); // 合計
System.out.println(sum); // 12
}
}
Java何をしているかを日本語にすると、
list.stream()
リストから「ストリーム(データの流れ)」を作る.filter(n -> n % 2 == 0)
条件に合う要素だけ通す(偶数だけ残す).mapToInt(n -> n)IntStream(int 専用のストリーム)に変換.sum()
流れてきた値を全部足す
というパイプラインになっています。
「何をしたいか」が左から右に素直に読めるのが Stream の一番の強みです。
Stream の基本構造:「中間操作」と「終端操作」
大きな流れをつかむ
Stream API は、ざっくり次の 3 ステップでできています。
- ストリームを「生成」する
- ストリームを「変形・フィルタ」する(中間操作)
- 最後に「結果を取り出す」(終端操作)
ここが分かると、かなり怖くなくなります。
1. ストリームの生成
代表的なパターンは次のようなものです。
list.stream()Arrays.stream(array)Stream.of(1, 2, 3)
例えば List からストリームを作るとき:
List<String> names = Arrays.asList("Alice", "Bob", "Carol");
names.stream() // ここで Stream<String> ができる
Javaここまでは「まだ何も実行されていない」とイメージしてください。
「これからこういう処理をするための準備」をしている段階です。
2. 中間操作(filter / map など)
中間操作は、「新しいストリームを返す操作」です。
代表的なものに filter、map、sorted などがあります。
例えば:
names.stream()
.filter(name -> name.length() <= 4) // 4文字以下
.map(name -> name.toUpperCase()) // 大文字に変換
Javaここまで書いても、まだ何も実行されません。
「こういうパイプラインを組み立てた」だけの状態です。
3. 終端操作(collect / forEach / count など)
最後に「終端操作」を呼ぶと、一気に実行されます。
代表例は collect、forEach、count、sum などです。
List<String> shortUpperNames =
names.stream()
.filter(name -> name.length() <= 4)
.map(name -> name.toUpperCase())
.toList(); // Java 16 以降の便利メソッド
JavatoList() が終端操作にあたり、
ここで初めて「ストリームが消費されて」「結果の List ができる」という流れになります。
この「終端操作を呼ぶまで実行されない」性質を、遅延評価 と呼びます。
よく使う中間操作をイメージでつかむ
filter:条件に合うものだけ通す
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evens =
list.stream()
.filter(n -> n % 2 == 0)
.toList(); // [2, 4]
Java「if で弾く」のではなく、「条件に合うものだけ流す」イメージです。
map:値を別の値に変換する
List<String> names = Arrays.asList("alice", "bob", "carol");
List<String> upper =
names.stream()
.map(s -> s.toUpperCase())
.toList(); // ["ALICE", "BOB", "CAROL"]
Javamap は「1 つの要素を別の値に変えて流す」操作です。
for 文で書いていた「新しいリストに add していく」作業をまとめて表現できます。
sorted:並び替える
List<String> sorted =
names.stream()
.sorted() // 自然順序でソート
.toList();
Javasorted(comparator) を使えば、Comparator で自由に順序も指定できます。
よく使う終端操作をイメージでつかむ
collect / toList:結果をコレクションに集める
Java 16 以降なら toList() が直感的です。
List<String> result =
names.stream()
.filter(name -> name.length() <= 3)
.toList();
JavaJava 8〜15 では Collectors.toList() を使います。
import java.util.stream.Collectors;
List<String> result =
names.stream()
.filter(name -> name.length() <= 3)
.collect(Collectors.toList());
JavaforEach:1 つずつ処理をする
names.stream()
.filter(name -> name.startsWith("A"))
.forEach(System.out::println);
JavaforEach は「結果をどこかに集めはしないが、副作用(表示、書き込みなど)を発生させる」終端操作です。
count / sum / average:集計系
long count =
names.stream()
.filter(name -> name.length() <= 3)
.count();
Java数値系は、mapToInt / mapToLong / mapToDouble を組み合わせることが多いです。
int totalLength =
names.stream()
.mapToInt(String::length)
.sum();
JavaStream API をいつ使うか・いつ使わないか
向いている場面
Stream が特に力を発揮するのは、
「コレクションや配列に対して、“フィルタ → 変換 → 集計” のような処理をする」場面です。
たとえば:
・条件に合う要素だけ抽出して、新しい List を作りたい
・あるプロパティだけ抜き出して、別の形の List にしたい
・条件を満たす件数を数えたい
・グルーピング、集計、最小値・最大値などを出したい
こういう処理は、for 文で書くより Stream で書いた方が
「何をしたいか」が一目で分かるコードになりやすいです。
まだ for 文で書いた方が分かりやすい場面
一方で、次のような場合は、無理に Stream にすると逆に読みにくくなりがちです。
ループの中で複雑な状態を更新する
ネストしたループや、早期 return / break がたくさん出てくる
「1 回だけ処理したい」ような単純なループ
初心者のうちは、
まず for 文で素直に書いてみる
→「これ Stream で書いたらスッキリしそう」と感じる箇所だけ、少しずつ置き換える
くらいのペースで十分です。
まとめ:Stream API の全体像を頭に定着させる
Stream API を、あらためてシンプルに整理するとこうです。
コレクションや配列など“要素の並び”に対して、
「何をしたいか」を filter / map / sorted / collect などの
“処理をつなげたパイプライン”として書く仕組み。
重要な感覚はこのあたりです。
list.stream()でストリームを生成filter/map/sortedなどで「中間操作」をつなげていくcollect/toList/forEach/count/sumなどの「終端操作」を呼んだ瞬間に一気に実行される(遅延評価)- for 文でやっていた「フィルタ → 変換 → 集計」のような処理を、宣言的に、短く、読みやすく書ける
