Skip to content

Instantly share code, notes, and snippets.

@asufana
Last active December 14, 2015 02:59
Show Gist options
  • Save asufana/c8e21e622d06719eb117 to your computer and use it in GitHub Desktop.
Save asufana/c8e21e622d06719eb117 to your computer and use it in GitHub Desktop.
Java8 StreamAPI入門

Java8 Stream API

コレクション操作を簡易に記述できる

参考:社内Java8勉強会 ラムダ式とストリームAPI

参考:@IT:ラムダ式で本領を発揮する関数型インターフェースとStream APIの基礎知識

サンプルコード

    //--- 基本 -------------------------------------------------

    @Test
    //forEach リスト内容を表示する
    public void testForEach() throws Exception {
        users.forEach(user -> System.out.println(user.name())); //それぞれ処理する
    }

    //--- 変換 -------------------------------------------------
    
    @Test
    //map 社員名リストに変換する
    public void testMap() throws Exception {
        final List<String> names = users
                //stream化する
                .stream()
                //map 要素すべてになんらかの処理をしてその結果から新しいリストを作成する
                .map(user -> user.name())
                //メソッド参照 .map(User::name) でも可
                .collect(Collectors.toList()); //まとめる
        
        //内容出力 .forEach(System.out::println) でも可
        names.forEach(user -> System.out.println(user));
    }

    //--- フィルタ -------------------------------------------------
    
    @Test
    //filter 特定部署の所属者を抽出する
    public void testFilter() throws Exception {
        users.stream()
            //条件で抽出する
            .filter(user -> user.branch().startsWith("情報システム部"))
            //内容出力
            .forEach(user -> System.out.println(user.name()));
    }

    //--- ユニーク/ソート -------------------------------------------------
    
    @Test
    //distinct 部署一覧
    public void testDistinct() throws Exception {
        users.stream()
            //部署リストに変換
            .map(user -> user.branch())
            //ユニークを取る
            .distinct()
            //部署名でソート
            .sorted()
            //内容出力
            .forEach(System.out::println);
    }

    @Test
    //sort 特定部署の所属者を抽出する
    public void testSort() throws Exception {
        users.stream()
            //条件抽出
            .filter(user -> user.branch().startsWith("情報システム部"))
            //ソート
            .sorted((x, y) -> x.branch().compareTo(y.branch()))
            //Comparatorのユーティリティメソッド使えば簡単
            //.sorted(Comparator.comparing(User::branch))
            //内容出力
            .forEach(user -> System.out.println(user.name() + ":" + user.branch()));
    }
    
    // --- 有無確認 ------------------------------

    @Test
    //optional 写真URL未設定のユーザの存在有無
    public void testOptional() throws Exception {
        final Optional<User> noUrlUser = users.stream()
                                              //写真URLが未設定のユーザを抽出
                                              .filter(user -> isEmpty(user.photoUrl()))
                                              //1件目を返却
                                              .findFirst();
        
        //Optionalで返却されるので、isPresent()関数で有無確認可能(仮に存在しなくてもnull返却されない)
        //Optional:nullの可能性がある値をラップして、より安全なプログラムが書けるようにする仕組み
        if (noUrlUser.isPresent()) {
            System.out.println("There is no url user.");
        }
    }

    @Test
    //anyMatch 写真URL未設定のユーザの存在有無(もっと簡単に書ける)
    public void testAnyMatch() throws Exception {
        //コレクション自体に存在チェック関数が用意された anyMatch, allMatch, noneMatch
        if(users.stream().anyMatch(user -> isEmpty(user.photoUrl()))){
            System.out.println("There is no url user.");
        }
    }
    
    // --- 応用編 -------------------------------

    @Test
    //groupingBy 部門所属ユーザ数
    public void testGrouping() throws Exception {
        users.stream()
            //groupingBy 部門をキーとしたMapを返す Map<String, List<User>>
            .collect(Collectors.groupingBy(User::branch))
            //Setコレクションを返す Set<Entry<String, List<Usre>>>
            .entrySet()
            //改めてストリーム化
            .stream()
            //部門名でソート
            .sorted((x, y) -> x.getKey().compareTo(y.getKey()))
            //部門名と所属者数を出力
            .forEach(x -> System.out.println(x.getKey() + ":" + x.getValue().size()));
    }
    
    @Test
    //Predicate 部門抽出条件を引数で渡す
    public void testPredicate() throws Exception {
        filterByFunction(user -> user.branch().startsWith("情報システム部"));
    }

    private void filterByFunction(Predicate<? super User> predicate) {
        users.stream()
            //条件抽出(filterは引数Predicate<T>を取る)
            .filter(predicate)
            //ソート
            .sorted(Comparator.comparing(User::branch))
            //内容出力
            .forEach(user -> System.out.println(user.name() + ":" + user.branch()));
    }

関数型インターフェース

参考:http://www.atmarkit.co.jp/ait/articles/1404/30/news017.html#012

実装時の注意

リスト操作が簡易になり、コード中にさらりとフィルタ処理など記述できるようになるが、コントローラ、モデル中などで直接リスト操作を記述しないこと。

述語やポリシーは特定のクラスにDRYで記述されていなければならない。

今後もリストラッパークラスを用意し、その中でStreamAPIを利用すること。当社の実装で言えば、LambdaJコード部分をStreamAPIに置き換える。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment