業務備忘録

備忘録です

【Java】StreamAPIの練習をする(2.flatMapの利用)

2023-08-29 23:40:35 | 日記

今回はstreamAPIで使えるflatMapの練習をします。

練習台として前回の記事で作成しておいた書籍の貸出アプリを使用しますが、
まずは前回から追記した点を記載。

1.変更点

User.java

public String getBookName() {
        return bookName;
    }

    public String getUserName() {
        return userName;
    }

    public Date getDate() {
        return date;
    }

User.javaのフィールドにgetterを追加。

Book.java

    public List<User> getHistory() {
        return history;
    }

貸出履歴保持用のリストにgetterを追加。

Proc.java

    public void lendProc(Map<String, Book> bookMap) {
        System.out.println("借りる本のタイトルを入力してください");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //貸出対象の書籍名
        String targetBookName;
       
        try {
            targetBookName = br.readLine();
            System.out.println(targetBookName + "を貸出します...");
           
            //貸出可能図書を管理している連想配列から貸出対象の本を取得
            Book targetBook = bookMap.get(targetBookName);
            targetBook.incrementLendCount();
            targetBook.setLendDate(new Date());
            targetBook.addHistory(new User(targetBookName,"simulacre", targetBook.getLendDate()));
            bookMap.put(targetBookName, targetBook);//Bookオブジェクトに対する処理を行った後に,パラメータであるbookMapに戻す
           
            System.out.println(targetBookName + "は" + targetBook.getLendCount() + "回貸し出されました");
       
           
            System.out.println("続いて貸出を行う場合は1を入力してください");
            System.out.println("貸出回数で並べ替えを行う場合は2を入力してください");
            System.out.println("貸出履歴を一覧表示する場合は3を入力してください");
           
            String mode = br.readLine();
           
            if(mode.equals("1")) {
                //貸出処理を再帰的に呼び出す
                lendProc(bookMap);
            }else if(mode.equals("2")) {
                //貸出順に表示する
                sortByLendCount(bookMap);
                return;
            }else if(mode.equals("3")) {
                showAllHistory(bookMap);
            }else {
                return;
            }
           
        } catch (IOException e) {
            e.printStackTrace();
        }

貸出対象の書籍(targetBook)に対する操作(貸出回数のインクリメント、貸出日の更新、貸出履歴の追加)を行ったあと、貸出可能書籍を保持しているhashMap、つまり引数として渡されてきたbookMapにtargetBookを返しています。

今回は、Bookオブジェクト内で、貸出履歴を保持しているhistoryに対しStreamAPIで操作を行います。
historyは、Userクラスで型付けされているArrayListです。つまり、BookMap→Bookオブジェクト→historyリスト→Userオブジェクト→Dateなど貸出日情報
という階層になっていて、素朴に考えると貸出日情報を取り出すにはまあまあ手間がかかりそうです。

今回追加したshowAllHistoryメソッド内でStreamAPIを用いてとりあえず貸出日順に表示してみます。

 

2.実際にやってみる

showAllHistoryメソッド

private void showAllHistory(Map<String, Book> bookMap) {
        List<Book> allHistoryList = new ArrayList<>();
        allHistoryList.addAll(bookMap.values());
        allHistoryList.stream().flatMap(s->s.getHistory().stream())
            .sorted((r1,r2)-> r2.getDate().compareTo(r1.getDate()))
            .forEach(t->System.out.println(t.getDate()));
    }

とりあえずhashMapのままだとStreamAPIを使用できないので、bookMapから、保持されているBookオブジェクトを全部取り出す必要があります。

Mapインターフェースにはvalues()メソッドが用意されており、values()はhashMapのすべての値をCollection※として返します。
※Collection…ListやSetなどの要素の集合を表すオブジェクトを規定しているインターフェース

addAllメソッドは、引数に指定したCollectionを呼び出し元のCollectionに追加するメソッドなので、

allHistoryList.addAll(bookMap.values());

この時点で、allHistoryListにはbookMap内のBookオブジェクトがすべて追加されています。

例えば『こころ』を2回借りだして、『斜陽』を1回借りだした場合、

AllHisytoryListと各オブジェクト間の関係はこのようになっています。
リスト内の各Bookオブジェクトは貸出履歴としてUserオブジェクトを保持していますが、Userオブジェクトを操作するには各BookオブジェクトのHistoryからUserオブジェクトを逐一取得して…もしくは、別にListを用意して、Historyリスト内のUserオブジェクトを追加していくような処理を経なくてはなりません。

そんなかったるい処理を書かずに、リストの中に存在するリストをひとまとめにして処理したい場合、flatMapメソッドを使用します。

allHistoryList.stream().flatMap(s->s.getHistory().stream())

この記述だけで、Userクラスに型付けされたStream,Streamが作成されており、Userオブジェクトを順次処理できるようになります。あとはsorted()を利用して並び替えを行いforEach()で各要素に終末処理を行うだけ。ともかくネストしたCollectionに対して便利に操作できるメソッドのようです。

↓処理結果

Wed Aug 30 23:20:10 JST 2023

Wed Aug 30 23:20:07 JST 2023

Wed Aug 30 23:20:03 JST 2023