Java Stream API 中間・終端操作の実践的使い方

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]

上記パターンを組み合わせることで、複雑なビジネスロジックも宣言的に記述でき、可読性と保守性が大幅に向上する。

タグ: Java Stream API filter map REDUCE

5月25日 15:39 投稿