Java 8以降で導入されたStream APIは、コレクションに対する宣言的な処理を可能にする。中間操作と終端操作を組み合わせることで、複雑なデータ変換も簡潔に記述できる。以下では、頻出する操作パターンをコード例と共に解説する。
中間操作一覧
| メソッド | 概要 |
|---|---|
| filter(Predicate) | 条件に合致する要素のみを残す |
| map(Function) | 各要素を別の型へ1:1で変換 |
| flatMap(Function) | 各要素を別の型へ1:Nで展開 |
| limit(long) | 先頭n件のみを取得 |
| skip(long) | 先頭n件をスキップ |
| concat(Stream,Stream) | 2つのストリームを連結 |
| distinct() | 重複要素を除去 |
| sorted(Comparator) | 指定ルールでソート |
| peek(Consumer) | デバッグ用途で各要素を覗く |
終端操作一覧
| メソッド | 概要 |
|---|---|
| count() | 最終要素数を返す |
| max(Comparator) | 最大値をOptionalで返す |
| min(Comparator) | 最小値をOptionalで返す |
| findFirst() | 最初にヒットした要素を返す |
| findAny() | 並列時に高速なヒット要素を返す |
| anyMatch(Predicate) | 1件でも条件に合致すればtrue |
| allMatch(Predicate) | 全件が条件に合致すればtrue |
| noneMatch(Predicate) | 全件が条件に合致しなければtrue |
| collect(Collector) | List/Mapなど任意の型へ集約 |
| toArray() | 配列へ変換 |
| iterator() | Iteratorを生成 |
| forEach(Consumer) | 副作用を伴う逐次処理 |
実践サンプルコード
1. filter・ソートの基本パターン
// 文字列リストからnull・空文字を除去し自然順ソート
List<String> raw = Arrays.asList("9", "2", null, "7", "", "2", "9");
List<String> cleaned = raw.stream()
.filter(Objects::nonNull)
.filter(s -> !s.isEmpty())
.distinct()
.sorted()
.collect(Collectors.toList());
// 結果: [2, 7, 9]
2. オブジェクトリストの条件抽出
class Employee {
String name;
int age;
BigDecimal salary;
// コンストラクタ・ゲッター略
}
List<Employee> staff = List.of(
new Employee("Alice", 25, new BigDecimal("300000")),
new Employee("Bob", 30, null),
new Employee("Carol", 22, new BigDecimal("250000"))
);
// 給与が存在する社員を年齢昇順で取得
List<Employee> valid = staff.stream()
.filter(e -> e.getSalary() != null)
.sorted(Comparator.comparing(Employee::getAge))
.collect(Collectors.toList());
3. Map<String,Object>の扱い
List<Map<String,Object>> rows = List.of(
Map.of("name", "Yamada", "age", 40, "score", 85),
Map.of("name", "Sato", "age", 32, "score", null)
);
// score が null でないレコードを score 降順でソート
List<Map> filtered = rows.stream()
.filter(m -> m.get("score") != null)
.sorted(
Comparator.comparing(
(Map<String,Object> m) -> (Integer) m.get("score")
).reversed()
)
.collect(Collectors.toList());
4. map・reduce による数値集計
// 文字列を数値へ変換し合計・最大・平均を算出
List<String> nums = List.of("3", "10", "7");
List<Integer> ints = nums.stream()
.map(Integer::valueOf)
.collect(Collectors.toList());
int total = ints.stream().reduce(0, Integer::sum);
int largest = ints.stream().max(Integer::compareTo).orElse(0);
double avg = ints.stream().mapToInt(Integer::intValue)
.average()
.orElse(0.0);
5. BigDecimal の一括変換
// 文字列リストを BigDecimal に変換し全要素に1加算
List<String> src = List.of("1.5", "2.3", "9.9");
List<BigDecimal> result = src.stream()
.map(BigDecimal::new)
.map(b -> b.add(BigDecimal.ONE))
.collect(Collectors.toList());
// 結果: [2.5, 3.3, 10.9]
上記パターンを組み合わせることで、複雑なビジネスロジックも宣言的に記述でき、可読性と保守性が大幅に向上する。