✅ はじめに:Java 8以降の開発では「リスト操作力」が問われる
Java 8以降、Stream APIの登場によってListの操作が劇的に効率化されました。
しかし──
✅ addAllやfor文と混在してコードが読みづらくなる
✅ mapとflatMapを使い分けられない
✅ filterの順序ミスでパフォーマンス劣化
✅ null対策が甘く実行時例外が発生
✅ collectの最適な書き方がわからない
…といった悩みを抱える開発者は少なくありません。
本記事では、**「プロが実務で使うリスト処理の実践術」**を、マージ・変換・フィルタリングに焦点を当てて徹底解説します。
✅ Stream APIの基本構造
✅ Streamは元のリストを壊さない(非破壊的)
✅ 中間操作は評価されず蓄積 → 終端操作で初めて実行
✅ メソッドチェーンで可読性UP
✅ 1. リストをマージ(結合)する3つの実践パターン
❗ 従来の書き方(非推奨)
✅ ① Stream.concat()
✅ ② flatMapを使った複数リスト統合
✅ リストを動的に渡す場合にも有効
✅ 2. リストの変換(map・flatMap)実践術
✅ map(1対1変換)
✅ flatMap(1対多変換に最適)
✅ 「map + 非Stream返却」ならmap
✅ 「map + Stream返却」ならflatMap
✅ 3. フィルタリング(filter)応用パターン
✅ 基本
✅ 複数条件
✅ Optional併用(null安全)
✅ 4. プロが多用する応用操作
| 操作 | 用途 | サンプル |
| distinct | 重複削除 | .distinct() |
| sorted | 並び替え | .sorted(Comparator.comparing(User::getAge)) |
| groupingBy | グループ化 | Collectors.groupingBy(User::getDepartment) |
| collectingAndThen | 集約後処理 | .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)) |
✅ Before / Afterで理解:IDの重複を削って並べる
❌ 従来(冗長)
✅ Stream版(たった1行)
✅ 5. パフォーマンス注意点
| NG | OK | 理由 |
| filter後にmap | map後にfilter | マッピングが無駄になる |
| distinct前にmap | map後にdistinct | 重複を早く削る |
| flatMap多用 | 必要時のみ | ネストが深いと重い |
| 無闇なparallel() | データ量が少ないと逆効果 | |
✅ 最後に:Stream APIは「読みやすく・壊さない」が正解
✅ 冗長なfor文はStreamで置き換え
✅ mapとflatMapは「1対1」「1対多」で使い分け
✅ filterは前処理に最適
✅ Optional併用でnull対策
✅ groupByやdistinctで実務効率化
「使える」から「読みやすい」「安全」「再現性のある」コードへ。
はじめに:Map操作、まだ「containsKey」で書いていませんか?
JavaでMapを使うとき、以下のようなコードを書いた経験はありませんか?
Java 8以前ではこれが一般的でした。しかしJava 8では、computeIfAbsentやmergeを使うことで、こうした冗長なコードをたった1行で表現できます!
本記事では、これらのメソッドの使い方から実践例まで徹底的に解説します。
✅ computeIfAbsentとは?
🔍 役割
キーが存在しないときに初期値を生成しMapに登録する。
📌 シグネチャ
✅ 使用例:リストを保持するMapに要素を追加する場合
📉 Before(従来のコード)
📈 After(computeIfAbsentで1行に)
👉 このように、存在確認不要でスッキリ!
✅ mergeとは?
🔍 役割
指定したキーに対して、既に値がある場合は「結合処理」を行い、新規ならそのまま値を格納。
📌 シグネチャ
✅ 使用例:カウントアップ処理
📉 Before(キー存在チェックあり)
📈 After(mergeで1行)
👉 キーがなければ1を設定。あれば現在値と1を足す。美しい!
✅ computeIfAbsentとmergeの使い分け
| ケース | 推奨メソッド |
| 値を「初期化してから操作」したい | computeIfAbsent |
| 「集計・加算・結合」したい | merge |
| ListやSetを使うMapに要素を追加したい | computeIfAbsent |
| 数値や文字列をまとめたい | merge |
✅ さらに活用例:文字列の出現回数カウント
📤 出力:
✅ Listを使ったグルーピングも簡単に!
📤 出力:
✅ まとめ:Map操作はJava 8で劇的にスマートになる!
| キーワード | 内容 |
| computeIfAbsent | 値がない場合のみ初期化 |
| merge | 既存値と競合処理(加算・結合など) |
| メリット | if文不要・コードが短く読みやすくなる |
■ 導入:Stream APIでコードを劇的に簡潔化
Java 8以降で導入された Stream API は、リストや配列の操作を「宣言的」「関数型スタイル」で記述できる強力な仕組みです。
従来の for ループを使った処理に比べて、コード量を大幅に削減し、バグを防止 できます。
本記事では、List 操作を中心に、Stream APIの実践サンプルを多数紹介します。
■ 基本構文:Streamの流れを理解する
Streamの基本構成は以下の3ステップです。
イメージ:
■ サンプル①:条件でフィルタリングする
例えば「偶数だけを抽出する」処理は、以下のように書けます。
| 処理内容 | コード例 | 説明 |
| 偶数のみ抽出 | filter(n -> n % 2 == 0) | 条件に一致する要素だけを残す |
■ サンプル②:要素を変換する(map)
全ての要素を2倍にする変換も簡単です。
| 処理内容 | コード例 | 出力例 |
| 2倍変換 | map(n -> n * 2) | [2, 4, 6, 8] |
■ サンプル③:ソート・並び替え
文字列リストをアルファベット順にソートする例です。
降順にする場合は:
| 処理内容 | メソッド | 説明 |
| 昇順ソート | sorted() | 自然順序(A→Z, 1→9) |
| 降順ソート | sorted(Comparator.reverseOrder()) | 逆順に並び替え |
■ サンプル④:重複を除去する(distinct)
| 処理内容 | メソッド | 効果 |
| 重複削除 | distinct() | 同一要素を1つにまとめる |
■ サンプル⑤:合計・平均・最大値を求める
数値リストの集計処理も簡単です。
| 処理 | メソッド | 結果型 |
| 合計 | sum() | int |
| 平均 | average() | OptionalDouble |
| 最大 | max() | OptionalInt |
■ サンプル⑥:複数条件の処理(filter + map)
| 処理順序 | 内容 |
| ① | Aで始まる要素のみ抽出 |
| ② | すべて大文字に変換 |
| ③ | 新しいリストに収集 |
■ サンプル⑦:グルーピング(groupingBy)
Stream APIでは、SQLのように「グループ化」も可能です。
| 処理内容 | メソッド | 結果 |
| 長さごとにグループ化 | groupingBy(String::length) | {3=[Tom, Ken], 4=[John]} |
■ サンプル⑧:並列処理で高速化(parallelStream)
大量データを高速処理したい場合は parallelStream() を使います。
ただし、順序が保証されない ため、結果の順序が重要な場合は通常の stream() を使用しましょう。
■ Stream APIを使うメリットまとめ
| メリット | 内容 |
| コードの簡潔化 | for文やif文のネストを削減 |
| 可読性向上 | 処理の流れが直感的に理解できる |
| パフォーマンス | 並列処理で大量データにも対応 |
| 安全性 | NullPointerExceptionを防ぎやすい |
■ まとめ:Stream APIを使いこなして効率化
Stream APIは一度慣れてしまえば、リスト処理を格段に楽にしてくれます。
特にJava 11以降では、ラムダ式やメソッド参照との相性も良く、業務アプリのコード品質を底上げできます。
「駑馬十駕」を信念に IT系情報を中心に調べた事をコツコツ綴っています。