概要
業務システムでは、データベースから取得した一覧を条件で絞り込み、画面表示用の DTO に変換する処理が頻繁に発生します。for ループとif文で書くと行数が膨らみ、可読性が下がります。Stream API の filter と map を使えば、データの流れを宣言的に記述でき、意図が伝わりやすいコードになります。この記事では、実務でよく使うパターンを中心に、filter と map の組み合わせ方を整理します。
使いどころ
社員一覧から特定部署のメンバーだけを抽出して画面用 DTO に変換する
受注データから未出荷の注文だけを絞り込んで出荷指示リストを作る
商品マスタから在庫ゼロの商品を除外して価格リストを出力する
コード例
import java.util.List;
public class StreamFilterMap {
record Employee(String name, String dept, int age) {}
record EmployeeDto(String name, int age) {}
public static void main(String[] args) {
var employees = List.of(
new Employee("田中", "開発", 28),
new Employee("鈴木", "営業", 35),
new Employee("佐藤", "開発", 42),
new Employee("高橋", "人事", 31)
);
var devMembers = employees.stream()
.filter(e -> "開発".equals(e.dept()))
.map(e -> new EmployeeDto(e.name(), e.age()))
.toList();
devMembers.forEach(System.out::println);
}
}Version Coverage
List.of で不変リスト初期化、toList() で collect 不要に。record とメソッド参照で記述が簡潔になる。
// Java 17: List.of + メソッド参照 + toList()
var names = List.of("Alice", "Bob", "Charlie");
var upper = names.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.toList();Library Comparison
注意点
Stream は1回しか消費できない。再利用したい場合は Supplier<Stream> を使う。
副作用のある処理(DB更新など)を map 内で行わない。forEach を使う。
null が混在するリストでは filter(Objects::nonNull) を先頭に入れると安全。
FAQ
先に filter で絞り込んでから map で変換する方が、変換処理の回数が減り効率的です。
チェック例外は Stream 内で直接 throw できないため、try-catch で包むか RuntimeException に変換します。
要素数が数万件以上で、各要素の処理が重い場合に効果があります。少量データでは逆にオーバーヘッドが生じます。