【Java】StreamAPIのmap()メソッドを完全理解!初心者向け徹底解説
はじめに
JavaのStreamAPIは、コレクションの操作を直感的かつ効率的に行える強力な機能です。その中でもmap()
メソッドは最も頻繁に使用される操作の一つです。
この記事では、map()
メソッドの基本的な使い方から実践的な活用例まで、初心者にもわかりやすく解説していきます。
map()メソッドとは?
map()
メソッドは、ストリームの各要素に対して指定した変換処理を適用し、新しいストリームを生成するメソッドです。
簡単に言うと:
- 元のデータを別の形に変換したい時に使用
- 一対一の変換処理(1つの入力に対して1つの出力)
- 元のストリームは変更されず、新しいストリームが作成される
基本的な構文
Stream<R> map(Function<? super T, ? extends R> mapper)
-
T
: 入力の型 -
R
: 出力の型 -
mapper
: 変換処理を定義する関数
実際のコード例で理解しよう
例1: 文字列を大文字に変換
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("alice", "bob", "charlie");
// 従来の書き方(for文)
List<String> upperNamesOld = new ArrayList<>();
for (String name : names) {
upperNamesOld.add(name.toUpperCase());
}
// Stream APIのmap()を使用
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperNames); // [ALICE, BOB, CHARLIE]
}
}
例2: 数値を2倍にする
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> doubledNumbers = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println(doubledNumbers); // [2, 4, 6, 8, 10]
例3: オブジェクトのプロパティを取得
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
public class PersonExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("田中", 25),
new Person("佐藤", 30),
new Person("鈴木", 35)
);
// 名前だけを取得
List<String> names = people.stream()
.map(Person::getName)
.collect(Collectors.toList());
// 年齢だけを取得
List<Integer> ages = people.stream()
.map(Person::getAge)
.collect(Collectors.toList());
System.out.println(names); // [田中, 佐藤, 鈴木]
System.out.println(ages); // [25, 30, 35]
}
}
ラムダ式とメソッド参照の使い分け
ラムダ式の場合
// 複雑な変換処理
List<String> formattedNames = people.stream()
.map(person -> person.getName() + "さん(" + person.getAge() + "歳)")
.collect(Collectors.toList());
メソッド参照の場合
// シンプルな変換処理
List<String> names = people.stream()
.map(Person::getName) // メソッド参照の方がスッキリ
.collect(Collectors.toList());
実践的な活用例
例1: CSVデータの変換
List<String> csvLines = Arrays.asList(
"田中,25,東京",
"佐藤,30,大阪",
"鈴木,35,名古屋"
);
List<Person> people = csvLines.stream()
.map(line -> {
String[] parts = line.split(",");
return new Person(parts[0], Integer.parseInt(parts[1]));
})
.collect(Collectors.toList());
例2: APIレスポンスの変換
class ApiResponse {
private String id;
private String title;
private boolean active;
// コンストラクタ、ゲッター等省略
}
class DisplayItem {
private String displayTitle;
private String status;
public DisplayItem(String title, boolean active) {
this.displayTitle = title.toUpperCase();
this.status = active ? "有効" : "無効";
}
// ゲッター等省略
}
// APIレスポンスを表示用オブジェクトに変換
List<ApiResponse> responses = getApiResponses();
List<DisplayItem> displayItems = responses.stream()
.map(response -> new DisplayItem(response.getTitle(), response.isActive()))
.collect(Collectors.toList());
map()と他のStream操作の組み合わせ
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<String> result = numbers.stream()
.filter(n -> n % 2 == 0) // 偶数のみフィルタ
.map(n -> n * n) // 二乗
.map(n -> "数値: " + n) // 文字列に変換
.collect(Collectors.toList());
System.out.println(result); // [数値: 4, 数値: 16, 数値: 36, 数値: 64, 数値: 100]
よくある間違いとその対処法
間違い1: nullチェックを忘れる
// 危険な例
List<String> names = people.stream()
.map(Person::getName) // nameがnullの場合NullPointerException
.collect(Collectors.toList());
// 安全な例
List<String> names = people.stream()
.map(person -> person.getName() != null ? person.getName() : "名前なし")
.collect(Collectors.toList());
// Optional使用例
List<String> names = people.stream()
.map(Person::getName)
.map(name -> Optional.ofNullable(name).orElse("名前なし"))
.collect(Collectors.toList());
間違い2: mapとflatMapの使い分け
// mapの場合(一対一変換)
List<String> words = Arrays.asList("hello", "world");
List<Integer> lengths = words.stream()
.map(String::length) // 各文字列の長さを取得
.collect(Collectors.toList());
// flatMapの場合(一対多変換)
List<String> sentences = Arrays.asList("hello world", "java stream");
List<String> allWords = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.collect(Collectors.toList());
パフォーマンスの考慮事項
1. 並列処理の活用
// 大量のデータを処理する場合
List<Integer> largeList = IntStream.range(1, 1000000)
.boxed()
.collect(Collectors.toList());
List<Integer> result = largeList.parallelStream() // 並列処理
.map(n -> n * n)
.collect(Collectors.toList());
2. 中間操作の最適化
// 効率的な書き方
List<String> result = people.stream()
.filter(person -> person.getAge() >= 20) // filterを先に
.map(Person::getName) // mapは後に
.collect(Collectors.toList());
まとめ
map()
メソッドは以下のような特徴があります:
- 一対一変換: 1つの入力に対して1つの出力
- 型変換可能: 異なる型に変換できる
- 関数型: ラムダ式やメソッド参照が使用可能
- 遅延評価: 終端操作が呼ばれるまで実行されない
使用する場面
- データの形式を変換したい時
- オブジェクトのプロパティを抽出したい時
- 計算結果を新しいリストにしたい時
注意点
- nullチェックを忘れずに
- flatMapとの使い分けを理解する
- パフォーマンスを考慮した書き方を心がける
Stream APIのmap()
メソッドをマスターすることで、より読みやすく保守性の高いJavaコードが書けるようになります。まずは簡単な例から始めて、徐々に複雑な処理にチャレンジしてみてください!