Java
勉強会

【社内勉強会】Javaの基礎 ~ ラムダ式とStream API ~ (2017/07/13)

0.はじめに


伝えたいこと

  • Java8のナウで便利な記法を知っておきましょう

前提知識

  • Javaのforループ、無名関数

参考図書

Java本格入門.jpg
『Java本格入門
~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで』技術評論社

特に指定がない限り、この本を引用しています。


目次


1. ラムダ式


ラムダ式とは

ラムダ式は、メソッドの引数などに処理そのものを渡すことができるかのような、強力な記法です。

※『Java 本格入門』P151 引用

List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, 3, 2, 1, 4, 5);

//ラムダ式を使わない書き方(無名クラス)
Collections.sort(list, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1, o2);
    }
});

//ラムダ式を使った書き方
Collections.sort(list,
        (o1, o2) -> Integer.compare(o1, o2)
);

ラムダ式は「無名クラスの実装メソッドが簡単に書ける」だけだが、後述のStream APIと組み合わせると、分かりやすい表記ができる。


[補足] 無名クラス

イベントリスナーの設定でよく使う。

//Androidで、クリックイベントを設定する場合の書き方
findViewById(R.id.hogehoge).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // クリック時の処理
    }
});

関数型インターフェイスの代替として使用する

  • 関数型インターフェース:抽象メソッドが1つのみのインターフェース
  • [Try] JavaDocを確認。 JavaDoc Comparator

  • 関数型インターフェースは、ラムダ式に置き換えられる


ラムダ式の基本文法

  • 基本文法:(引数) -> { 処理}
  • 条件によって、いろいろ省略できる
    • 引数部分の型
    • 引数の丸括弧(引数が1個のとき)
    • returnと波括弧(処理が1つのとき)
//ラムダ式の基本文法
Collections.sort(list,
        (Integer o1, Integer o2) -> {
           return Integer.compare(o1, o2);
        }
);

//引数の型を省略
Collections.sort(list,
        (o1, o2) -> {
           return Integer.compare(o1, o2);
        }
);

//引数の型と、return、波括弧を省略
Collections.sort(list,
        (o1, o2) -> Integer.compare(o1, o2)
);

メソッド参照

メソッドそのものも代入できる。

List<String> list = Arrays.asList("X", "Y", "Z");
//メソッド参照を使った書き方
list.forEach(System.out::println);

//ラムダ式を使った書き方
list.forEach(str -> System.out.println(str));

forEachメソッドの引数は、java.util.function.Consumerという関数型インターフェース。


2.Stream API


Stream APIとは?

Stream APIは、大量データを逐次処理する「ストリーム処理」を効率的に記述するための手段として導入されました。

※『Java 本格入門』 P150引用

List<Integer> list = Arrays.asList(100, 60, 30, 50, 70);

list.stream() //Streamインスタンス生成
    .filter(s -> s>=70) //中間操作。//70以上を抽出。
    .forEach(s -> System.out.println(s)); //終端操作。

TODO Streamの画像を表示

※ 参考サイト
http://www.oracle.com/technetwork/jp/articles/java/ma14-java-se-8-streams-2177646-ja.html


Stream APIの特長

Stream APIは「HowではなくWhatを記述するAPI」。

  • How: 個別の処理
  • What: 処理の目的

  • Stream APIはループ処理を書かずに、目的を書く

  • SQLと同じ考え。


Stream APIの分類

  • Stream作成
  • Streamの中間操作
  • Streamの終端操作

Stream API一覧


Streamを作成するAPI

//ListやSetからStreamを作成
List<Integer> list = Arrays.asList(100, 60, 30, 50, 70);
list.stream() //Streamインスタンス生成
    .forEach(System.out::println);


//数値範囲からStreamを作成
IntStream.range(1, 5) //末尾を含まない
    .forEach(System.out::println);

IntStream.rangeClosed(1, 5) //末尾を含む
    .forEach(System.out::println);


Streamに対する中間操作

List<Integer> list = Arrays.asList(10, 6, 3, 5, 7);

//map: 要素を別の値に置き換える
list.stream()
    .map(s -> s * s)
    .forEach(System.out::println);

//filter: 条件に合致した要素のみ絞り込む
list.stream()
    .filter(s -> s>=7)
    .forEach(System.out::println);

//sort:
list.stream()
    .sorted((s1,s2) -> s2 - s1) //降順
    .forEach(System.out::println);


Streamに対する終端操作

List<Integer> list = Arrays.asList(10, 6, 3, 5, 7);

//forEach: 要素に対してアクションを実行する
list.stream()
    .forEach(System.out::println);

//collect: 結果を作成する
List<Integer> newList = list.stream()
    .filter(s -> s>=7)
    .collect(Collectors.toList());

//average: 平均を返す(集計操作)
list.stream()
    .average();


関数型言語

JavaScriptも。。。