はじめに
前半編では、Stream APIの基本である
- filter
- map
- sum / average / count
といった 単純な集計処理 を扱いました。
後半編では、実務でよく使われる以下の内容に挑戦します。
- 条件に合うデータが「存在するか」
- 並び替え(ソート)
- グループ化して集計する
Stream APIが「for文の代わり」ではなく、
業務データを安全かつ簡潔に処理するための仕組みであることを意識しながら解説します。
使用するデータ
class Employee {
private String name;
private String department;
private int age;
private int salary;
public Employee(String name, String department, int age, int salary) {
this.name = name;
this.department = department;
this.age = age;
this.salary = salary;
}
public String getName() { return name; }
public String getDepartment() { return department; }
public int getAge() { return age; }
public int getSalary() { return salary; }
}
public class Main {
List<Employee> employees = List.of(
new Employee("佐藤", "営業", 28, 400000),
new Employee("鈴木", "営業", 35, 550000),
new Employee("高橋", "開発", 30, 600000),
new Employee("田中", "開発", 45, 800000),
new Employee("伊藤", "人事", 32, 450000)
);
}
問題6:人事部の社員が存在するか確認する
回答コード
employees.stream()
.anyMatch(e -> "人事".equals(e.getDepartment()));
出力結果
true
解説
この問題は「1人でも条件に合う人がいるか」を調べたいケースです。
anyMatchとは?
- 条件に一致する要素が 1つでもあれば true
- 見つかった時点で処理を終了(高速)
for文で書くと?
boolean exists = false;
for (Employee e : employees) {
if ("人事".equals(e.getDepartment())) {
exists = true;
break;
}
}
→ Stream APIでは 1行で安全に書ける のがメリットです。
問題7:給与が高い順に「社員名と年齢」を並べる
回答コード
employees.stream()
.sorted(Comparator.comparing(Employee::getSalary).reversed())
.map(e -> Map.entry(e.getName(), e.getAge()))
.toList();
出力結果
[田中=45, 高橋=30, 鈴木=35, 伊藤=32, 佐藤=28]
解説
この問題では、複数の処理を順番に組み合わせています。
処理の流れ
-
sorted
→ 給与で並び替え -
reversed()
→ 高い順に変更 -
map
→ Employee →「名前+年齢」に変換 -
toList()
→ Listとして取得
Map.entryとは?
Map.entry("田中", 45)
これは
- 「キー:名前」
- 「値:年齢」
を持つ 簡易的なペア(Entry) を作るための仕組みです。
問題8:最年長の社員を取得する
回答コード
employees.stream()
.sorted(Comparator.comparing(Employee::getAge).reversed())
.map(e -> Map.entry(e.getName(), e.getAge()))
.findFirst();
出力結果
Optional[田中=45]
解説
ポイントは findFirst()
- Streamの 最初の要素を取得
- データが存在しない可能性があるため Optional になる
処理の考え方
- 年齢で降順ソート
- 一番最初=最年長
- それを取得
Optionalが返る理由
Streamは
- データが0件
- フィルターで全件除外
というケースもあるため、
「必ず存在する」とは言えない設計 になっています。
問題9:部署ごとの平均給与を求める
回答コード
employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));
出力結果
{営業=475000.0, 開発=700000.0, 人事=450000.0}
解説
この問題は 実務で非常によく使われます。
groupingByとは?
- キーごとにデータをまとめる
- 今回は「部署名」がキー
averagingDoubleとは?
- 指定した数値の平均を計算
- 結果は double
結果の意味
営業 → 平均475,000円
開発 → 平均700,000円
人事 → 平均450,000円
SQLでいうと
SELECT department, AVG(salary)
FROM employee
GROUP BY department;
とほぼ同じです。
問題10:開発部のみの平均給与を求める
回答コード
employees.stream()
.filter(e -> "開発".equals(e.getDepartment()))
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));
出力結果
{開発=700000.0}
解説
問題9との違い
- 先に
filterで 対象を絞っている
実務での使い方
- 特定部署
- 特定条件(30歳以上など)
- 特定期間
などを 前処理でfilter してから集計するのが定番です。
後半まとめ
後半で学んだ重要ポイントは以下です。
-
anyMatch:存在チェック -
sorted+Comparator:並び替え -
findFirst:先頭要素取得 -
groupingBy:部署別・分類別の集計
Stream APIは
「業務データを読みやすく、安全に処理するための道具」 です。
おわりに
IT初心者のうちは
- for文で考える
- Streamで書き直す
- 処理の流れを日本語で説明できるか確認する
このステップを踏むのが最短ルートです。
Stream APIに慣れると、
コードレビューでも「分かりやすい」と評価されやすくなります。