forEach とラムダって何者か(「ちょっとした処理」を読みやすく書く道具)
forEach とラムダ式は、「コレクションの全要素に対して簡単な処理をする」場面で、コードを短く・読みやすく書くための道具です。 従来の for 文でも同じことはできますが、ラムダを使うと「何をしたいか」にフォーカスした書き方になり、ノイズが減ります。 まずは「拡張 for 文との違い」を意識しながら、基本形を押さえていきましょう。
拡張 for 文との比較から入る
拡張 for 文の基本形
まず、従来の「拡張 for 文」で全要素を処理する形を確認します。
List<String> names = List.of("Taro", "Hanako", "Jiro");
for (String name : names) {
System.out.println(name);
}
Java「names の中身を先頭から順番に取り出して、name に入れて処理する」という、とても素直な書き方です。 これに対して、forEach とラムダを使うと、次のように書けます。
names.forEach(name -> System.out.println(name));
Javaどちらも「全要素を表示する」という意味ですが、forEach 版は「names に対して、要素ごとにこの処理をしてね」と、 “処理の中身”だけをラムダ式として渡している形になります。
ラムダ式の基本形を分解する
「引数」→「矢印」→「処理」
ラムダ式の基本形は、こういう見た目です。
name -> System.out.println(name)
Javaこれを分解すると、
name … 1件分の要素を受け取る引数 -> … 「この引数に対して、右側の処理を実行する」という矢印 System.out.println(name) … 実際にやりたい処理
という構造になっています。 拡張 for 文で書くときの「for (String name : names) の name」が、そのままラムダの引数になっているイメージです。
もう少し複雑な処理でも、基本は同じです。
names.forEach(name -> {
String upper = name.toUpperCase();
System.out.println("NAME: " + upper);
});
Javaここでは、ラムダの右側を { ... } で囲んで、複数行の処理を書いています。 「1件分の要素を受け取って、ブロック内で好きな処理をする」という形が見えればOKです。
forEach とラムダで「意図が読みやすくなる」例
ログ出力の簡単な処理
例えば、ユーザーIDの一覧をログに出したいとします。
List<Integer> userIds = List.of(101, 102, 103);
Java拡張 for 文ならこうです。
for (Integer id : userIds) {
System.out.println("userId=" + id);
}
JavaforEach とラムダを使うと、こう書けます。
userIds.forEach(id -> System.out.println("userId=" + id));
Java「userIds に対して、1件ずつこの処理をする」という構造が、 forEach というメソッド名とラムダの形から、かなりストレートに伝わります。
条件付きの処理も書ける
ラムダの中身は普通の Java コードなので、if 文も書けます。
userIds.forEach(id -> {
if (id >= 102) {
System.out.println("VIP userId=" + id);
}
});
Java「全件を回しながら、条件を満たすものだけログに出す」という意図が、 拡張 for 文と同じように書けつつ、forEach によって“全件処理している”ことが明示されます。
ラムダ式で意識してほしい「簡単さ」のライン
「1行〜数行の“ちょっとした処理”」に向いている
ラムダ式は、「1行〜数行の簡単な処理」を書くときに特に威力を発揮します。
names.forEach(name -> System.out.println(name.toUpperCase()));
Javaこのくらいなら、拡張 for 文よりも「何をしたいか」が一目で分かります。 一方で、ラムダの中が長くなりすぎると、逆に読みづらくなります。
names.forEach(name -> {
// ここに10行以上の複雑な処理がずらっと並ぶ…
});
Javaこうなってきたら、「ラムダの中身を別メソッドに切り出す」方が読みやすくなります。
names.forEach(this::printProcessedName);
private void printProcessedName(String name) {
// 複雑な処理をここに書く
}
Java「ラムダは簡単な処理」「複雑になったらメソッド参照や普通のメソッド」に切り替える、 という感覚を持っておくと、コードの可読性を保ちやすくなります。
まとめと小さな練習
forEach とラムダは、「コレクションの全要素に対して簡単な処理をする」ための、読みやすい書き方です。 拡張 for 文と同じことができつつ、「全件処理している」「1件分の要素に対してこの処理をする」という意図が、コードから直接伝わりやすくなります。
練習として、
List<String> の名前一覧を作り、拡張 for 文版と forEach+ラムダ版の両方で「大文字にして表示する」コードを書いてみる List<Integer> の点数一覧から、60点未満だけ「不合格」と表示する処理を、ラムダの中に if を書いて実装してみる
という2つをやってみると、「ラムダの基本形」「forEach の使いどころ」がかなり体に入ってくるはずです。
