拡張 for 文(for-each)と Stream API(ラムダ式) の違い・使い分けは、実務で必ず押さえておくべきポイントです。
ここでは以下の流れで分かりやすく整理します。
全体構成
- 【基礎】for-each と Stream の思想の違い
- 【書き換え例①】出力処理
- 【書き換え例②】フィルタ処理(条件で抽出)
- 【書き換え例③】集計処理(合計・平均)
- 【書き換え例④】オブジェクト加工(DTO変換)
- 【まとめ】どっちを使う?実務判断チャート
1. 基礎:for-each と Stream の思想の違い
| 観点 | 拡張 for 文 | Stream API |
|---|---|---|
| 書き方 | 命令的(どう処理するかを書く) | 宣言的(何をしたいかを書く) |
| 特徴 | わかりやすく、直感的 | 簡潔で強力、関数型スタイル |
| 処理 | 手続き的に順に実行 | メソッドチェーンで処理を流す |
| 並列化 | 自前でスレッド制御が必要 | parallelStream() で簡単並列化 |
| 主な用途 | 簡単な走査・入門・小規模処理 | フィルタ・集計・変換・大量データ |
2. 書き換え例①:出力処理(for → Stream)
拡張 for 文版
List<String> names = List.of("Suzuki", "Tanaka", "Sato");
for (String name : names) {
System.out.println(name);
}
JavaStream API 版
names.forEach(System.out::println);
Java🗝️ ポイント:
forEachは Stream の中でも使えるが、List自体にもforEachがある。- 処理が1行で完結。副作用(printlnなど)を行う場合にシンプル。
3. 書き換え例②:フィルタ処理(条件で抽出)
拡張 for 文版
List<String> list = List.of("apple", "banana", "avocado");
List<String> result = new ArrayList<>();
for (String s : list) {
if (s.startsWith("a")) {
result.add(s);
}
}
System.out.println(result); // [apple, avocado]
JavaStream API 版
List<String> result = list.stream()
.filter(s -> s.startsWith("a"))
.toList();
System.out.println(result);
Java🗝️ ポイント:
filter()が「条件で選ぶ」操作。toList()で結果をリスト化(Java 16+)。forに比べて 「何をしたいか」 が明確。
4. 書き換え例③:集計処理(合計・平均)
拡張 for 文版
int[] scores = {80, 90, 75, 100};
int sum = 0;
for (int s : scores) {
sum += s;
}
System.out.println("合計: " + sum);
JavaStream API 版
int sum = Arrays.stream(scores)
.sum();
System.out.println("合計: " + sum);
Java🗝️ ポイント:
Arrays.stream()で配列を Stream 化。- 合計・平均・最大最小はすでにメソッドが用意されている:
.sum(),.average(),.max(),.min()
5. 書き換え例④:オブジェクト変換(DTO → ViewModel)
拡張 for 文版
class User {
String name;
int age;
User(String n, int a) { name = n; age = a; }
}
List<User> users = List.of(
new User("Suzuki", 30),
new User("Tanaka", 20),
new User("Sato", 25)
);
List<String> names = new ArrayList<>();
for (User u : users) {
names.add(u.name.toUpperCase());
}
System.out.println(names); // [SUZUKI, TANAKA, SATO]
JavaStream API 版
List<String> names = users.stream()
.map(u -> u.name.toUpperCase())
.toList();
System.out.println(names);
Java🗝️ ポイント:
map()は要素を「別の形に変換」する処理。- DTOの変換、JSON整形、画面表示用データの生成などに最適。
6. 書き換え例⑤:条件抽出+集計(複合処理)
拡張 for 文版
List<Integer> nums = List.of(3, 10, 7, 2, 12);
int sum = 0;
for (int n : nums) {
if (n >= 5) {
sum += n;
}
}
System.out.println(sum); // 29
JavaStream API 版
int sum = nums.stream()
.filter(n -> n >= 5)
.mapToInt(Integer::intValue)
.sum();
System.out.println(sum);
Java🗝️ ポイント:
filter→mapToInt→sumのように、パイプラインで流れを表現できる。forよりも「意図(5以上を合計)」が明確。
7. 並列処理(拡張forでは困難なこと)
Stream API の強み
大量データを扱うとき、拡張 for では逐次処理だが、Stream なら簡単に並列化できます:
list.parallelStream()
.filter(x -> x.length() > 3)
.forEach(System.out::println);
Java🗝️ 注意点:
- 並列処理(
parallelStream())は順序保証がなくなる。 - IO処理や状態を持つ操作(書き込みなど)には不向き。
8. 実務での使い分け(まとめ表)
| 処理のタイプ | 拡張 for 文 | Stream API |
|---|---|---|
| 単純な出力・ログ | ◎(可読性が高い) | ○ |
| 条件でフィルタ | ○ | ◎(filterが明確) |
| 集計(合計・平均など) | △ | ◎ |
| オブジェクト変換 | △ | ◎(mapが便利) |
| ネストループ | ◎ | △(読みにくくなる) |
| 並列処理 | ✕ | ◎(parallelStream) |
| 小規模処理 | ◎ | ○ |
| 大規模データ処理 | △ | ◎ |
実務判断チャート
▼ 配列やリストを順に処理したい
└→ インデックスが必要? → はい → 通常の for
↓いいえ
└→ シンプルな処理? → はい → 拡張 for
↓いいえ(加工・集計・条件分岐が多い)
└→ Stream API
まとめ(実務ベストプラクティス)
- 単純な繰り返し → 拡張 for 文でOK(読みやすくて安全)
- 集計・変換・条件抽出などの“処理の流れ” → Stream API が強い
- チーム開発では:
- 小規模・明快なループ → 拡張 for
- 中~大規模・変換+集計 → Stream
- どちらも混在OK。
⇒ 「ループの目的を最も明確に書ける方法」を選ぶのが正解。
