StreamAPIを使用したコレクションの処理について学んだこと

# 目的：学習したことの整理・アウトプット

　
**今回扱うクラスの処理の目的**：Readerメソッドで取得した情報をユーザーごとに分けて請求額を計算（Readerで得た情報の処理）

**流れ**
ユーザーごとに(UserIdで)に請求情報をまとめて管理
　　　⇩
全部の請求(finalPaymentAmount)をリストから取り出す
　　　⇩
支払方法に応じて、請求金額の合計を計算
　　　⇩
請求額の合計をアウトプット(Writerメソッドへ)

### ItemProcessorの処理全体
※個別の処理の解説は下へ
```ItemProcessor.java
@Bean
public ItemProcessor<List<BillingRecord>, List<UserTotalBilledAmount>> processor() {
    return billingRecords -> {
        // ユーザーごとに請求記録をグループ化
        Map<Integer, List<BillingRecord>> groupedByUser = billingRecords.stream()
            .collect(Collectors.groupingBy(BillingRecord::getUserId));

        // 結果リストの準備
        List<UserTotalBilledAmount> results = new ArrayList<>();

        // グループごとに処理
        for (Map.Entry<Integer, List<BillingRecord>> entry : groupedByUser.entrySet()) {
            // ユーザー情報の取得
            int userId = entry.getKey();
            String userName = entry.getValue().get(0).getUserName();
            String creditCardNumber = entry.getValue().get(0).getCreditCardNumber();

            // 請求金額の合計を計算
            double totalAmount = 0.0;
            for (BillingRecord record : entry.getValue()) {
                double amount = record.getFinalPaymentAmount();
                switch (record.getPaymentId()) {
                    case 1:
                        totalAmount += amount;      // 全額加算
                        break;
                    case 6:
                        totalAmount += amount / 6;  // 6分の1を加算
                        break;
                    case 12:
                        totalAmount += amount / 12; // 12分の1を加算
                        break;
                    default:
                        totalAmount += 0;           // 何もしない
                        break;
                }
            }

            // 結果オブジェクトを作成してリストに追加
            UserTotalBilledAmount userTotal = new UserTotalBilledAmount(userId, userName, creditCardNumber, totalAmount);
            results.add(userTotal);
        }

        // 結果を返却
        return results;
    };
}
```

# やりたいこと(目的)と手段
**目的１**
ユーザーごとに請求情報一覧をまとめたい。
　　⇩　言い換えると、、
ユーザーIdとその請求額を**セットで**まとめたい。

```
Map<Integer, List<BillingRecord>> groupedByUser = billingRecords.stream()
            .collect(Collectors.groupingBy(BillingRecord::getUserId));
```
　
**手段(抽象度_高)**
１. Mapで管理する
２. コレクションの種類を変換する
※処理順番は2.=>1.

**手段(具体度_高)**
1. **.stream()**を使用してコレクション生成
2. コレクションに対して、**groupingByメソッド**で**カテゴリー分け＆ユーザーIdと請求情報の紐づけ**

　
参考
[Stream APIの基本](https://java-code.jp/1501 "Stream APIによる処理は、大雑把に以下のように分類できます。
・データソースからStreamを生成
・加工／フィルターなどの中間処理
・最終的な集計／出力などの終端処理")
[groupingByメソッドの使い方](https://enterprisegeeks.hatenablog.com/entry/2014/06/19/093000#:~:text=partitioningBy%2C%20groupingBy,%22%20%3D%20%22%20%2B%20v%3B "partitioningBy, groupingByメソッド")
[ラムダ式の使い方](https://en-ambi.com/itcontents/entry/2019/04/25/103000/#%E3%83%A9%E3%83%A0%E3%83%80%E5%BC%8F%E3%81%AE%E5%9F%BA%E6%9C%AC:~:text=%E3%81%84%E3%82%8B%E3%81%A7%E3%81%97%E3%82%87%E3%81%86%E3%80%82-,List%3CInteger%3E%20numbers%20%3D%20List.of(3%2C%201%2C%20%2D4%2C%201%2C%20%2D5%2C%209%2C%20%2D2%2C%206%2C%205%2C%203%2C%205)%3B%0Anumbers.stream()%0A%20%20%20%20%20%20%20%20.filter(number%20%2D%3E%20Math.abs(number)%20%3E%3D%205)%0A%20%20%20%20%20%20%20%20.forEach(System.out%3A%3Aprintln)%3B,-%E5%BC%95%E6%95%B0%E3%81%AE%E5%80%A4 "BillingRecord::getUserId部分の記述について、ラムダ式とStream APIで学ぶモダンJava")

　
**目的２**
マップから**キーと値を取りだし**、ユーザーId、ユーザー名、クレジットカード番号、当該月の請求額**合計**を**セットで管理**したい。

```
List<UserTotalBilledAmount> results = new ArrayList<>();

for (Map.Entry<Integer, List<BillingRecord>> entry : groupedByUser.entrySet()) {
            
            int userId = entry.getKey();
            String userName = entry.getValue().get(0).getUserName();
            String creditCardNumber = entry.getValue().get(0).getCreditCardNumber();

            (中略) 
            
            UserTotalBilledAmount userTotal = new UserTotalBilledAmount(userId, userName, creditCardNumber, totalAmount);
            results.add(userTotal);
        }

        return results;

```

**手段(抽象度_高)**
1. 上記４項目を保持するオブジェクトを格納するためのリストを用意
2. Mapから４項目を取得
マップに含まれるキーと値のマッピングのセットを取得する
3. 請求額合計を支払い方法に応じて合計・計算（中略部分）
4. 「1.」で作成したリストへ格納
5. 「４.」のリストを出力

　
**手段(具体度_高)**
1. 略
2. Mapから４項目を取得
entrySet()メソッドと拡張forを利用してキーと値のセットを取得する
Ⅰ.ユーザーId ＝ キーなので **getKey()メソッド**で取得
Ⅱ.ユーザー名とクレジットカード番号は、 **getValue().get(0).getフィールド名()メソッド**で取得
**※ユーザーIdでグループ化した請求情報リスト内のユーザー名とクレカ番号は、同じユーザーに属するため、get(0)でリストの先頭を指定すればよい**
3. 省略
4. インスタンス化＆フィールド変数をセット
5. addメソッドでリストへ追加

　
参考
[拡張for文を利用したMapのキーと値の取得](https://www.techiedelight.com/ja/iterate-map-in-java-using-entryset/#google_vignette "entrySet()メソッドを使用してJavaでマップを反復")
[Mapで使用する主要メソッド](https://dtnavi.tcdigital.jp/cat_web/web_015/)
[Iteratorとは](https://workteria.forward-soft.co.jp/blog/detail/10082 "Iterator とは？List や Set との関係や Map での使い方")

　
**目的３**
支払方法に応じて当月の請求を計算する。(1,6,12回払いの3種類)
```
 switch (record.getPaymentId()) {
                    case 1:
                        totalAmount += amount;      // 全額加算
                        break;
                    case 6:
                        totalAmount += amount / 6;  // 6分の1を加算
                        break;
                    case 12:
                        totalAmount += amount / 12; // 12分の1を加算
                        break;
                    default:
                        totalAmount += 0;           // 何もしない
                        break;
                }
```

特定のデータの値パターンをチェックしたい場合は「switch文」を使う。
基本は条件分岐はIf文で対応すべきだが、場合によってはswitch文の方が可読性が高くなる


　
# 参考
https://java-code.jp/1501
https://enterprisegeeks.hatenablog.com/entry/2014/06/19/093000#:~:text=partitioningBy%2C%20groupingBy,%22%20%3D%20%22%20%2B%20v%3B
https://en-ambi.com/itcontents/entry/2019/04/25/103000/#%E3%83%A9%E3%83%A0%E3%83%80%E5%BC%8F%E3%81%AE%E5%9F%BA%E6%9C%AC:~:text=%E3%81%84%E3%82%8B%E3%81%A7%E3%81%97%E3%82%87%E3%81%86%E3%80%82-,List%3CInteger%3E%20numbers%20%3D%20List.of(3%2C%201%2C%20%2D4%2C%201%2C%20%2D5%2C%209%2C%20%2D2%2C%206%2C%205%2C%203%2C%205)%3B%0Anumbers.stream()%0A%20%20%20%20%20%20%20%20.filter(number%20%2D%3E%20Math.abs(number)%20%3E%3D%205)%0A%20%20%20%20%20%20%20%20.forEach(System.out%3A%3Aprintln)%3B,-%E5%BC%95%E6%95%B0%E3%81%AE%E5%80%A4
https://www.techiedelight.com/ja/iterate-map-in-java-using-entryset/#google_vignette
https://dtnavi.tcdigital.jp/cat_web/web_015/
https://workteria.forward-soft.co.jp/blog/detail/10082