目的
Java8で導入されたラムダとStreamAPIについていろいろ調べたのでメモする(すべてJavaの話です)
ラムダ式
- ラムダ式は、関数型インターフェースを使ってラムダ計算を実装する構文
- 関数型インターフェースは、一つのAbstractメソッドしか持たないインターフェースのこと
- Comparator/Runnable/Function/Predicateなど
- ラムダ計算は、識別子/無名関数/関数適用の3つの要素だけで式が表現される計算モデル
- JavaではJVMで直接関数を扱うのではなく、クラスのインスタンスを使って表現している
- 関数型インターフェースは、一つのAbstractメソッドしか持たないインターフェースのこと
- 型推論で変数の型定義が省略できる。
- 省略しなくてもいい。
- 省略されすぎると、見慣れないため意味がわからない。
- そのうち慣れる。
- 省略されすぎると、見慣れないため意味がわからない。
- 省略しなくてもいい。
- クロージャーではない。
- ローカル変数は実質的にfinalな変数にしかアクセスできない。(変更できない)
- 独自のスコープは持たない。(ローカル変数と同じ変数名は利用できない)
- 無名内部クラス(匿名クラス)はラムダ式に置き換え可能(ほぼ問題なく(thisの扱いが違う))
- 似ているが実行時の呼び出しが異なる。
- ラムダは実行時にクラスが生成される
- 匿名クラスのようにコンパイル時にクラスを生成するとクラスロードが遅くなるため
- 2回目以降の呼び出しは速い
- 匿名クラスのようにコンパイル時にクラスを生成するとクラスロードが遅くなるため
- ラムダのインスタンスは、VMが最適な生成方法を選択するらしい。
- JVMの実装によっては今後も改善される可能性あり。
- ラムダは実行時にクラスが生成される
- 似ているが実行時の呼び出しが異なる。
StreamAPI
- ラムダ式と組み合わせて使うパイプライン型のデータ処理を実現するAPI
- メソッドチェーン形式で記述
- ソース、中間操作、終端操作の3つの構成要素からなる。
- ソース:
list.stream()
(1つだけ) - 中間操作:
.map(User::name)
(複数指定可能) - 終端操作:
.collect(Collectors.toList());
(1つだけ)
- ソース:
- 並列化が容易
- StreamAPIを利用しないで並列処理を書く場合にくらべコード量が減る
- 遅延評価で処理速度アップ
メリット
- コード量が減る
- より直感的にコードを理解できる
- 並列処理を気軽に実装できる
- 現在の基盤の資源を最大限に活かすためには並列処理が必要
デメリット
- forの構文とラムダ式が混ざるとコードの可読性は下がる。
- すべてラムダで表現しようとするとコードの可読性は下がる(と思っている)
- 並列化して速度が必ずしも上がるわけではない。(デメリットではないが)
- 標準の関数型インターフェースを利用する場合は、例外を投げれないのでラムダの中で処理しなければならない。
参考文献
実はこれが大事
まずはざっと理解する
- この資料のStreamAPIまでの章を理解しておけば、うんちくが語れます。
ひしだま's 技術メモページ Javaラムダ式 - レイアウトがWeb1.0で読みにくいが実はわかりやすい
きしだのはてな Java8 Lambdaの文法拡張まとめ - なんとなく頭に入りやすい。
より深く知る(概念系)
Java Day Tokyo Lambda: A Peek Under The Hood
- ラムダと内部クラスの違いをInvokedynamicからのバイトコード呼び出しとそのパフォーマンスを混じえて説明
IBM DeveloperWorks Java 8 言語での変更内容 - 文章が多い。さらなるうんちくをえるためには参考になった。
Java Day Tokyo Javaの関数型言語への挑戦/ベターオブジェクト指向言語からの脱皮 - Javaのラムダと関数型言語との違いをわかりやすく説明
関数型言語のウソとホント - Javaには関係なく関数型プログラミング言語に関する記事。
- これを読むとJavaのラムダが中途半端で申し訳なくも感じる。
実用編
とにかく書き方を知りたい
ラムダ式と関数型インタフェース
Java8 StreamAPI
今更人に聞けないJava5からの新機能(Java SE 8)
(o1, o2) -> o1 - o2 なんて呪文はもうやめて! - Java8でのComparatorの使い方
- ソートについてはこれはわかりやすい