Javaのラムダ式でListを抽出&変換!filterとmapでスマートに操作
生徒
「Javaでリストから条件に合う要素だけ取り出したり、リストの中身を変換したいんですが、どうすればいいですか?」
先生
「それならラムダ式とfilter、mapを使えば、効率よく抽出や変換ができますよ。」
生徒
「filterとmapってよく聞くけど、具体的な違いや使い方をちゃんと覚えたいです!」
先生
「それじゃあ、Listの抽出と変換について順番に見ていきましょう。」
1. filterメソッドとは?Listから条件で要素を抽出する方法
JavaのStream APIにおけるfilterメソッドは、リストの中から特定の条件に一致する要素だけを取り出す処理に使います。ラムダ式を組み合わせることで、読みやすくシンプルなコードになります。
例えば「文字列の長さが5文字以上の要素だけを抽出したい」といった場合、filterを使えば非常に簡単に実装できます。プログラミング初心者でも直感的に理解できるので、まずは少しずつ条件を変えて試してみると、データ抽出の感覚をつかみやすくなります。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FilterExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Sato", "Suzuki", "Tanaka", "Yamada");
// 文字列の長さが6文字以上の要素だけを抽出
List<String> filtered = names.stream()
.filter(name -> name.length() >= 6)
.collect(Collectors.toList());
System.out.println("抽出された名前:" + filtered);
}
}
抽出された名前:[Suzuki, Yamada]
このようにfilterは条件に合う要素だけを残す処理なので、リストの中から必要なデータだけを効率的に抽出できます。簡単な条件から始めて慣れることで、複雑な条件の絞り込みも無理なく扱えるようになります。
2. mapメソッドとは?Listの要素を別の形式に変換する方法
mapメソッドは、リストの各要素を別の形式に変換したいときに使います。文字列を大文字に変えたり、オブジェクトの一部のフィールドだけを取り出す場合などに活躍します。
List<String> upper = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upper);
[SATO, SUZUKI, TANAKA, YAMADA]
このようにmapは要素を「変換する」役割を持っています。ラムダ式と組み合わせることで、自由度の高い変換処理ができます。
3. filterとmapを組み合わせて効率的にデータ処理
filterとmapは一緒に使うことができます。条件で抽出したあとに変換を行うことで、コードがスッキリまとまります。
List<String> result = names.stream()
.filter(name -> name.length() >= 6)
.map(String::toLowerCase)
.collect(Collectors.toList());
System.out.println(result);
[suzuki, yamada]
この例では「6文字以上の名前だけを小文字に変換する」処理をfilterとmapで実現しています。ラムダ式を使えば、このような複雑な処理も1行ずつで記述できて可読性も良好です。
4. ListからMapへ変換する:Collectors.toMapの活用
JavaではリストをMapに変換したいことがあります。その場合、Stream APIのCollectors.toMapを使うことで簡単に実現できます。
import java.util.Map;
Map<String, Integer> nameLengthMap = names.stream()
.collect(Collectors.toMap(
name -> name,
name -> name.length()
));
System.out.println(nameLengthMap);
{Sato=4, Suzuki=6, Tanaka=6, Yamada=6}
このように、リスト内の要素をキーとして、長さやIDなどをバリューにすることで、検索しやすい形式に変換できます。
5. 重複キーがある場合の対処:マージ戦略を指定する
toMapを使うときにキーが重複していると例外が発生します。そのような場合は「マージ戦略」を指定して重複時の動作を定義することが重要です。
List<String> cities = Arrays.asList("Tokyo", "Osaka", "Tokyo");
Map<String, Integer> cityCount = cities.stream()
.collect(Collectors.toMap(
city -> city,
city -> 1,
Integer::sum // 重複時は合計
));
System.out.println(cityCount);
{Tokyo=2, Osaka=1}
Integer::sumを指定することで、同じキーが出現したときに値を合計するように処理できます。実務でもよく使うテクニックです。
6. オブジェクトListからMapへの変換例:ユーザーIDと名前を抽出
クラスのリストをMapに変換する場合も、ラムダ式とCollectors.toMapの組み合わせが効果的です。
class User {
int id;
String name;
User(int id, String name) {
this.id = id;
this.name = name;
}
int getId() { return id; }
String getName() { return name; }
}
List<User> users = Arrays.asList(
new User(1, "Alice"),
new User(2, "Bob"),
new User(3, "Charlie")
);
Map<Integer, String> userMap = users.stream()
.collect(Collectors.toMap(
User::getId,
User::getName
));
System.out.println(userMap);
{1=Alice, 2=Bob, 3=Charlie}
このように、オブジェクトのリストから特定のプロパティだけをMap形式に変換するのも簡単です。
まとめ
Javaのラムダ式を活用したList操作は、filterによる抽出、mapによる変換、Collectors.toMapによるMap変換、重複キー処理、そしてオブジェクトListからのMap生成など、多様な用途に応用できる強力な仕組みです。とくにStream APIは、長年Javaで使われてきたfor文やループ処理を大幅に簡潔化し、読みやすく整ったコードを書く助けとなります。ラムダ式を自然に使いこなすことで、条件抽出・値変換・要素の加工・集約といった典型的な処理が直感的に記述できるようになります。 filterは「条件に合うものだけを残す」役割を担い、mapは「要素を別の形に変換する」ために使われます。この二つを組み合わせることで、複雑なデータ処理も連続した一つの流れとして処理することができ、意図が明確なコードになるという利点があります。また、Collectors.toMapを使えば、リストの中身をキーとバリューをもつMapへと自在に変換できるため、データ検索の効率化にもつながります。重複キーの扱いにはマージ戦略が必要ですが、それもラムダ式で直感的に指定できるため、柔軟性の高いデータ加工が可能です。 さらに、クラスのリストからIDや名前などのプロパティを抽出し、Mapとして扱う方法は実務で非常に重宝されます。大量データの整形、APIレスポンスの加工、DBデータの集約など、多くの場面で応用できる考え方であり、ラムダ式とStream APIを正しく理解しておくことで効率的なコード設計が実現できます。 以下に、記事内容を総括するサンプルコードを改めて整理し、振り返りとして掲載します。
ラムダ式とStream APIを使った総合サンプル
// 文字列Listに対するfilterとmapの組み合わせ
List<String> names = Arrays.asList("Sato", "Suzuki", "Tanaka", "Yamada");
List<String> processed = names.stream()
.filter(name -> name.length() >= 6)
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(processed);
// 出力: [SUZUKI, YAMADA]
// ListからMapへの変換例
Map<String, Integer> nameLengthMap = names.stream()
.collect(Collectors.toMap(
n -> n,
n -> n.length()
));
// ユーザーオブジェクトのリストからMapに変換
class User {
int id;
String name;
User(int id, String name) { this.id = id; this.name = name; }
int getId() { return id; }
String getName() { return name; }
}
List<User> users = Arrays.asList(
new User(1, "Alice"),
new User(2, "Bob"),
new User(3, "Charlie")
);
Map<Integer, String> userMap = users.stream()
.collect(Collectors.toMap(
User::getId,
User::getName
));
生徒:「filterとmapって、ただ便利なメソッドというだけじゃなくて、流れとしてつなげるとすごく読みやすいコードになるんですね!」
先生:「そうなんだ。Stream APIは“処理の流れ”を記述するための仕組みだから、ラムダ式と相性が良いんだよ。複雑なロジックも簡潔に書けるようになる。」
生徒:「ListからMapに変換する方法も実務でよく使いそうですね。特にIDをキーにするパターン、すごくわかりやすかったです!」
先生:「データ加工では定番の考え方だから、早めに慣れておくと後で役に立つよ。重複キーの扱いも重要だから忘れずにね。」
生徒:「はい!filter → map → collect の流れも、自分の中でしっかり整理できました。次はもっと複雑な加工にも挑戦してみたいです!」
先生:「その調子。ラムダ式とStream APIは奥が深いから、いろいろ試しながら理解を深めていこう。」