SlideShare une entreprise Scribd logo
1  sur  54
Télécharger pour lire hors ligne
1 / 54
Java8基礎勉強会
ラムダ式とストリームAPI
2014年3月25日
アリエル・ネットワーク 池添
2 / 54
本日のテーマ
3 / 54
for文を駆逐してやる!
この世から1つ残らず!
4 / 54
目次
• 概要
• ラムダ式の基礎
• ストリームAPIの基礎
• ストリームAPIの拡張
5 / 54
ラムダ式とストリームAPI
• ラムダ式とは関数を簡便に表現するための記法。
• ストリームAPIは、ラムダ式を利用したコレク
ション操作用のAPI
• 関数型プログラミング言語由来。歴史は古い。
• これまでの手続き型やオブジェクト指向的なプ
ログラミング手法から、関数型プログラミング
に変わります。
• パラダイムシフトのよかん!!
6 / 54
簡単なサンプル
• フルーツの一覧の中から
• 名前が“りんご”で始まり、
• 値段が100円以上のものを、
• 値段順で並び替え、
• 名前だけを取り出して、
• リストを作成する
1 List<String> apples = fruits.stream()
2 .filter(f -> f.getName().startsWith("りんご"))
3 .filter(f -> f.getPrice() > 100)
4 .sorted(Comparator.comparingInt(Fruit::getPrice))
5 .map(Fruit::getName)
6 .collect(Collectors.toList());
7 / 54
メリット
• 手続き的だった記述が宣言的になる
• 保守性の向上…?
• 可読性の向上…?
• 簡単に並列実行できるようになる
8 / 54
保守性が向上する?
データの取
得
データの取
得
条件による
抽出
条件による
抽出
データの加
工
データの加
工
コンテナへ
の登録
コンテナへ
の登録
for文for文
デー
タの
初期
化
デー
タの
取得
条件による抽出条件による抽出
データの加工
コンテナへの
登録
\ ごちゃっ / パイプライン的ですっきり
• 処理の追加・削除・順番の入れ替えなどがやり
やすい。
• 再利用性やテスタビリティも向上するかも。
9 / 54
可読性は?
• 野球選手の一覧から、チームごとの投手の平均
年俸を取得する処理の例。
1 Map<String, Double> m = players.stream()
2 .collect(Collectors.groupingBy(player -> player.getTeam()))
3 .entrySet()
4 .stream()
5 .collect(Collectors.toMap(
6 entry -> entry.getKey(),
7 entry -> entry.getValue().stream()
8 .filter(player -> player.getPosition().equals("投手"))
9 .mapToInt(player -> player.getSalary())
10 .average()
11 .orElse(0)
12 )
13 );
• 気をつけないとすぐに読みにくくなる。
• stream()とかstream()とかstream()とかノイズが多い。
10 / 54
ラムダ式の基礎
11 / 54
むかしばなし
Javaでdelegate型を扱えるようにしたで!
J++って言いますねん。
互換性のないものはJavaとは呼べません。
提訴します!
なんとまあセンスの悪い言語設計でしょう。
まともな言語設計者ならポリシーが
許さないと思います。
じゃあ、自分らで言語つくりますわ。
C#って言うやつ。
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで。 2005年
Microsoft
Sun
某研究者
12 / 54
なぜラムダ式が必要になったのか
• 非同期処理や並列処理が当たり前に使われるよ
うになり、ラムダ式の必要性が高まった。
• Microsoftの提案を受け入れていれば、ラムダ
式がもっと早く入っていたかもしれない。
• でも、15年も先を見越して言語設計するなん
てことは難しい。
13 / 54
ラムダ式
• 関数を第一級オブジェクトとして扱えるように
なった。
• JVMで関数を直接扱えるようになったわけでは
なく、内部的にはクラスのインスタンスを使っ
て表現している。
14 / 54
ラムダ式の書き方
() -> 123
x -> x * x
(x, y) -> x + y
(int x, int y) -> {
return x + y;
}
いろいろ省略できる!
仮引数や戻り値の型はほとんど
型推論してくれる。かしこい!
15 / 54
ラムダ式の使い方
• ラムダ式を渡される側
void hoge(Function<Integer, Integer> func) {
Integer ret = func.apply(42);
}
• ラムダ式を渡す側
hoge(x -> x * x);
関数型インタフェース
16 / 54
関数型インタフェース
• ラムダ式の型は関数型インタフェースで表現さ
れる。
• 関数型インタフェースとは
• 実装するべきメソッドを1つだけ持ってる
interface
• @FunctionalInterfaceアノテーションを付ける
と、コンパイル時にメソッドが1つだけかどうか
チェックしてくれる。つけなくてもよい。
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
17 / 54
標準の関数型インタフェース
• Runnable, Consumer, Function, Predicate,Supplier
• BiConsumer,BiFunction,BiPredicate
• BooleanSupplier
• IntBinaryOperator,IntConsumer,IntFunction,IntPre
dicate,IntSupplier,IntToDoubleFunction,IntToLong
Function,IntUnaryOperator,ObjIntConsumer,ToIntBi
Function,ToIntFunction
• LongBinaryOperator,LongConsumer,LongFunction,Lon
gPredicate,LongSupplier,LongToDoubleFunction,Lon
gToIntFunction,LongUnaryOperator,ObjLongConsumer
,ToLongBiFunction,ToLongFunction
• DoubleBinaryOperator,DoubleConsumer,DoubleFuncti
on,DoublePredicate,DoubleSupplier,DoubleToIntFun
ction,DoubleToLongFunction,DoubleUnaryOperator,O
bjDoubleConsumer,ToDoubleBiFunction,ToDoubleFunc
tion
18 / 54
代表的な関数型インタフェース
• Runnable: 引数なし、戻り値なし
• Consumer: 引数1つ、戻り値なし
• Function: 引数1つ、戻り値あり
• Predicate: 引数1つ、戻り値がboolean
• Supplier: 引数なし、戻り値あり
• Bi + Xxxxx: 引数が2つ
19 / 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変
数のみアクセス可能。
実質的なfinalの変数のみ
アクセス可能。
エンクロージングイン
スタンスの参照
必ず持っている。 必要がなければ持たない。
メモリリークがおきにくい。
クラスファイルの生成 コンパイル時にクラ
スが生成される。
実行時にクラスが生成され
る。
クラスのロード時間が短縮
されるかも。
インスタンスの生成 明示的にnewする。 JVMが最適な生成方法を選
択する。
20 / 54
Javaのラムダ式はクロージャではない
• ローカル変数は、実質的にfinalな変数にしか
アクセスできない。
• 独自のスコープは持たず、外のスコープを引き
継ぐ。
• エンクロージングインスタンスの参照は、基本
持たない。
21 / 54
ラムダ式のスコープ
class Outer {
public void func() {
final int a = 0;
int b = 1;
list.stream().forEach(x -> {
int a = 2;
System.out.println(b);
});
b = 3;
}
}
値の変更が行われるローカル
変数はアクセス不可
独自のスコープを持たな
いので、変数名の衝突が
起きる。
明示しなければ、エンクロー
ジングインスタンスの参照を
持たない。
22 / 54
例外は苦手かも
• ラムダ式からチェック例外のあるAPIを呼び出す
場合
• 関数型インタフェースの定義にthrowsを記述する
(標準の関数型インタフェースにはついてない)
• ラムダ式の中でtry-catchを書く
list.map(x -> {
try {
// チェック例外のある呼び出し
} catch(XxxException ex) {
// エラー処理。
}
}).collect(Collectors.toList());
23 / 54
メソッド参照
• ラムダ式だけでなく、既存のメソッドも関数型
インタフェースで受け取ることが可能。
// ラムダ式を使った場合
list.forEach(x -> System.out.println(x));
// メソッド参照を使った場合
list.forEach(System.out::println);
fruits.map(fruit -> fruit.getName());
// インスタンスメソッドの参照もOK
fruits.map(Fruit::getName);
24 / 54
Lambda Expression Deep Dive
• ラムダ式は無名内部クラスのシンタックスシュガーじゃな
い。
• コンパイル時ではなく実行時にクラスが生成される。
• invokeDynamic命令を使っている。
• ラムダ式をコンパイルするとどんなバイトコードが生成さ
れるかみてみよう。
対象のコードはこんな感じ
public class Main {
public void main(){
Sample sample = new Sample();
sample.func(x -> x * x);
}
}
25 / 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()Ljava/util/function/IntFunction; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory()
// arguments:
(I)Ljava/lang/Object;.class,
// handle kind 0x6 : INVOKESTATIC
Main.lambda$main$0((I)Ljava/lang/Integer;)
, (I)Ljava/lang/Integer;.class
]
BIPUSH 12
INVOKEVIRTUAL Sample.func (Ljava/util/function/IntFunction;I)I
// ・・・途中省略・・・
private static lambda$main$0(I)Ljava/lang/Integer;
L0
// ラムダ式の中の処理 x -> x * x
ラムダのオブジェクトをスタックに積んで
メソッドを呼び出す
ラムダ式の
オブジェクト
をつくる命令
ラムダ式の
なかみ
26 / 54
ラムダ式の実行時の挙動
Main.classMain.class
コンパイル時に
生成されるもの
invoke dynamicinvoke dynamic
static method
lambda$main$0
static method
lambda$main$0
LambdaMetafactory
#metafactory
LambdaMetafactory
#metafactory
ラムダのインス
タンスをつくる
メソッド
ラムダのインス
タンスをつくる
メソッド
class Lambda$1
関数型インタフェー
スを実装
lambda$main$0
を呼び出す
class Lambda$1
関数型インタフェー
スを実装
lambda$main$0
を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し
(ブートストラップ)
2回目以降
の呼び出し
(Method
Handle)
作成 Mainの内部クラス
として作成
new
27 / 54
なぜこんな複雑なことを?
• コンパイル時にクラスを大量につくると、クラ
スロードのときに遅くなるかもしれないから。
とくにストリームAPIではラムダ式を大量につ
かうので。
• invokeDynamicを使うとパフォーマンスをあ
まり落とさず動的な処理が実現できる。
• 今後JVMの実装が変わると、インスタンスの生
成方法がより効率的なものになるかも。
28 / 54
ストリームAPIの基礎
29 / 54
ストリームAPIとは
• パイプライン型のデータ処理のAPI
• 絞り込み、データ変換、グループ化、集計など
の操作をそれぞれ分離した形で記述できる。
• 絞り込みの条件や、加工方法などをラムダ式で
指定する。
• メソッドチェイン形式で記述できる。
30 / 54
ストリームパイプライン
• ストリームパイプラインの構成要素
• ソース(Source)
• 中間操作(Intermediate Operation)
• 終端操作(Terminal Operation)
• ストリームパイプラインは、1つ以上のソース、
0個以上の中間操作、1つの終端操作から構成
される。
• 1つのStreamに対して複数の終端操作(いわゆ
る分配)をおこなってはいけない。
• 終端操作の結果をソースとして処理を継続する
ことも可能。
31 / 54
サンプル
• 1行目がソース
• 2行目から5行目までが中間操作
• 6行目が終端操作
1 List<String> apples = fruits.stream()
2 .filter(f -> f.getName().startsWith("りんご"))
3 .filter(f -> f.getPrice() > 100)
4 .sorted(Comparator.comparingInt(Fruit::getPrice))
5 .map(Fruit::getName)
6 .collect(Collectors.toList());
32 / 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ
以降の処理は実
行しない
ソースの要素を
1つずつ中間操作
に流す
終端操作を呼び出したときに
初めて全体の処理が動く。
それまで中間操作は実行され
ない。
ソース
終端操作
を実行
33 / 54
ソース
• 既存のデータからStream型のオブジェクトを
つくる。
• Streamの種類
• Stream<T>
• IntStream, LongStream, DoubleStream
• つくりかた
• Collection#stream
• Arrays#stream
• Stream#of
• BufferReader#lines
• IntStream#range
34 / 54
ソースの特性
• Sequential, Parallel
• 逐次実行か、並列実行か。
• Stream#parallelとStream#sequentialで相互に
変換可能。
• Ordered, Unordered
• Listや配列などはOrdered, SetなどはUnordered
• Stream#unorderedで順序を保証しないStreamに
変換可能。
• 無限リスト
35 / 54
中間操作
• 絞り込みや写像などの操作を指定して、新しい
Streamを返す。
• 遅延実行
• 処理方法を指定するだけで、実際には処理しない。
• 中間操作を呼び出すたびにループしてたら効率が悪い。
終端操作が呼ばれたときに、複数の中間操作をまとめて
ループ処理。
• 処理の種類
• 絞り込み: filter
• 写像: map, flatMap
• 並び替え: sorted
• 数の制御: limit, skip
• 同一要素除外: distinct
• tee的なもの: peek
36 / 54
特殊な中間操作
• ステートフルな中間操作:distinct, sorted
• 中間操作は基本的にステートレスだが、ステートフ
ルなものもある。
• 無限リストや並列処理のときに注意が必要。
• ショートサーキット評価な中間操作:limit
• 指定された数の要素を流したら処理を打ち切る。
• 無限Streamを有限Streamにしてくれる。
• 副作用向け中間操作:peek
• 中間操作では基本的に副作用するべきでない。
• デバッグやログ出力用途以外にはなるべく使わない
ようにしよう。
37 / 54
終端操作
• ストリームパイプラインを実行して、なんらか
の結果を取得する処理。
forEachだけは戻り値を返さない。副作用専用
のメソッド。
• 処理の種類
• たたみ込み:collect, reduce
• 集計:min, max, average, sum, count
• 単一の値の取得:findFirst, findAny
• 条件:allMatch, anyMatch, noneMatch
• 繰り返し:forEach, forEachOrdered
38 / 54
汎用的な終端操作:collect
• 引数にCollectorを指定して様々な処理がおこなえる。
• 集計:
• averagingInt, averagingLong, averagingDouble
• summingInt, summingLong, summingDouble
• counting
• maxBy, minBy
• summarizing
• グループ化
• groupingBy, partitioningBy
• コンテナに累積
• toList, toMap, toSet
• 結合
• joining
• たたみ込み
• reducing
39 / 54
Optionalを返す終端操作
• Optional
• nullチェックめんどくさい・・・。パイプラインの途
中でnullが現れると流れが止まってしまう。
• nullを駆逐し(ry
• Java8ではOptional型が入った。
• Stream APIの中にはOptionalを返す終端操作
がある。
• min, max, average, sum, findFirst,
findAny, reduceなど。
• 空のリストに対してこれらの処理を呼び出すと
Optional.emptyを返す。
40 / 54
ショートサーキット評価の終端操作
• ショートサーキット評価をする終端操作
• anyMatch, allMatch, noneMatch, findFirst,
findAny
• 例えば、Stream#countは全要素をループで回
すので時間がかかるが、Stream#findAnyは
ショートサーキット評価なので、1つめの要素
が見つかればすぐに終わる。
if(stream.count() != 0)
if(stream.findAny().isPresent())
41 / 54
並列処理
• すごく簡単に並列化できる。ソースを生成するときに、
Collection#parallelStreamや, Stream#parallel
を使うだけ。
• 順序保証
• ソースがORDEREDであれば並列実行しても順番は保証される。
• ただし、順序を保つために内部にバッファリングするので、
あまりパフォーマンスはよくない。
• 順番を気にしないのであれば、unorderedすると効率がよく
なる。
• 副作用に注意
• 中間操作で副作用が生じる場合はちゃんとロックしよう。
• ただし、ロックすると並列で実行しても待ちが多くなるので、
パフォーマンスはよくない。
• スレッドセーフでないArrayListなども並列実行可能。
42 / 54
並列処理のやりかた
• parallel()をつけるだけ!
• ただし、上記のような例ではあまり並列処理の
うまみはない。
1 List<String> apples = fruits.stream().parallel()
2 .filter(f -> f.getName().startsWith("りんご"))
3 .filter(f -> f.getPrice() > 100)
4 .sorted(Comparator.comparingInt(Fruit::getPrice))
5 .map(Fruit::getName)
6 .collect(Collectors.toList());
43 / 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに
異なるスレッドで中間
操作を並列に実行
ソースを複数に分割
spliterator
順序を保証する
場合は内部で
バッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの
実行結果を結合終端操作
44 / 54
ストリームAPIの拡張
45 / 54
ストリームAPIは機能不足?
• なんかいろいろ足りない!
• 他の言語と比べて標準で用意されてる機能が少ない。
• 複数のStreamを合成するzipすらないなんて…
• 毎回stream()を呼ぶのめんどくさい…
• 足りないならつくればいいじゃない。
• 関数型言語なら関数をどんどん増やせばよい。
C#なら拡張メソッドという仕組みがある。
• でも、JavaではStreamに自由にメソッドを増やせ
ない!こまった!
46 / 54
ストリームAPIの拡張ポイント
• たたみ込みを使えばたいていの処理はつくれる。
• 汎用的なCollectorをつくって、再利用できる
ようにしておくとよさそう。
• CollectorをつくるためのヘルパーAPIが用意
されている。既存のCollectorを組み合わせた
り、一から新しい終端操作をつくったりするこ
とができる。
47 / 54
Collectorの構成要素
• supplier
• コンテナの初期値を生成する人
• accumulator
• 値を加工してコンテナに格納する人
• combiner
• 並列で実行された場合、コンテナを結合する人
• finisher
• 最後にコンテナを加工する人
48 / 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ
結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 / 54
Collectorのつくり方
• Collector.of
• supplier, accumulator, combiner, finisher
を指定して新しいCollectorをつくる
• Collectors.mapMerger
• Collectorにcombinerを追加する
• Collectors.mapping
• accumulatorの前に実行される写像処理を追加す
る
• Collectors.collectingAndThen
• Collectorにfinisherを追加する
50 / 54
Collectorを使った例
• チームごとの投手の平均年俸を取得する
• 2つのCollectorを用意
• グループ化したストリームを返すCollector
• グループごとの平均値を返すCollector
1 Map<String, Double> averageSalaryMap = players.stream()
2 .filter(player -> player.getPosition().equals("投手"))
3 .collect(groupedStreamCollector(Player::getTeam))
4 .collect(averagePerGroupCollector(Player::getSalary));
51 / 54
Collectorの実装
1 static <T, V> Collector<Entry<V, List<T>>, ?, Map<V, Double>>
2 averagePerGroupCollector(ToIntFunction<T> mapper) {
3 return Collector.of(
4 () -> new HashMap<>(),
5 (map, entry) -> {
6 entry.getValue().stream()
7 .mapToInt(mapper)
8 .average()
9 .ifPresent(ave -> map.put(entry.getKey(), ave));
10 },
11 (left, right) -> {
12 left.putAll(right);
13 return left;
14 }
15 );
16 }
17 static <T, K> Collector<T, ?, Stream<Entry<K, List<T>>>>
18 groupedStreamCollector(Function<T, ? extends K> mapper) {
19 return Collectors.collectingAndThen(
20 Collectors.groupingBy(mapper), map -> map.entrySet().stream());
21 }
accumulator
combiner
finisher
• つくるのは難しいけど、汎用化しておけばいろ
いろなところで使える。
supplier
52 / 54
まとめ
• ラムダ式
• 関数を簡便に表記するための記法。
• デメリットも少ないし使わない手はないよね。
• ストリームAPI
• 慣れるまでは読み書きも難しいし、変なバグを生み
やすいかもしれない。
• でも慣れると少ない記述で複雑な処理が実現できる。
そして何より書いていて楽しい!
• しかし、標準で用意されてる機能が少ないのに、拡
張が難しいのはどうにかならんかね?
• 今後はオレオレコレクションやStreamUtilsクラ
スが乱立する可能性も!?
53 / 54
おまけ
• Java8を使うときはIntelliJ IDEAがおすすめ。
(EclipseのJava8正式対応は2014年6月)
• Java8の文法にもしっかり対応(たまーに型推
論間違えたりするけど)
• 無名内部クラスを書いてると、ラムダ式に書き
換えてくれる。
• for文を書いてると、ストリームAPIに書き換
えてくれる。(複雑な処理はムリだけど)
54 / 54
参考
• JavaのLambdaの裏事情
• http://www.slideshare.net/nowokay/java-2898601
• ラムダと invokedynamic の蜜月
• http://www.slideshare.net/miyakawataku/lambda-meetsinvokedynamic
• 倭マン's BLOG
• http://waman.hatenablog.com/category/Java8
• ラムダ禁止について本気出して考えてみた - 9つのパターンで見る
Stream API
• http://acro-engineer.hatenablog.com/entry/2013/12/16/235900
• Collectorを征す者はStream APIを征す(部分的に)
• http://blog.exoego.net/2013/12/control-collector-to-rule-stream-
api.html
• きしだのはてな
• http://d.hatena.ne.jp/nowokay/searchdiary?word=%2A%5Bjava8%5D
• 徹底解説!Project Lambdaのすべて returns
• http://www.slideshare.net/bitter_fox/java8-launchJava
• SE 8 lambdaで変わるプログラミングスタイル
• http://pt.slideshare.net/nowokay/lambdajava-se-8-lambda

Contenu connexe

Tendances

今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)NTT DATA Technology & Innovation
 
ドメイン駆動設計のための Spring の上手な使い方
ドメイン駆動設計のための Spring の上手な使い方ドメイン駆動設計のための Spring の上手な使い方
ドメイン駆動設計のための Spring の上手な使い方増田 亨
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」Takuto Wada
 
SQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するかSQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するかShogo Wakayama
 
入社1年目のプログラミング初心者がSpringを学ぶための手引き
入社1年目のプログラミング初心者がSpringを学ぶための手引き入社1年目のプログラミング初心者がSpringを学ぶための手引き
入社1年目のプログラミング初心者がSpringを学ぶための手引き土岐 孝平
 
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービューMasatoshi Tada
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門泰 増田
 
REST API のコツ
REST API のコツREST API のコツ
REST API のコツpospome
 
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~infinite_loop
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐkwatch
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション土岐 孝平
 
Spring Bootをはじめる時にやるべき10のこと
Spring Bootをはじめる時にやるべき10のことSpring Bootをはじめる時にやるべき10のこと
Spring Bootをはじめる時にやるべき10のこと心 谷本
 
データベース設計徹底指南
データベース設計徹底指南データベース設計徹底指南
データベース設計徹底指南Mikiya Okuno
 
今さら聞けないDiとspring
今さら聞けないDiとspring今さら聞けないDiとspring
今さら聞けないDiとspring土岐 孝平
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意Yoshitaka Kawashima
 
ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?Yoshitaka Kawashima
 
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)NTT DATA Technology & Innovation
 
Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Masahito Zembutsu
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Taku Miyakawa
 

Tendances (20)

今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
 
ドメイン駆動設計のための Spring の上手な使い方
ドメイン駆動設計のための Spring の上手な使い方ドメイン駆動設計のための Spring の上手な使い方
ドメイン駆動設計のための Spring の上手な使い方
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
SQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するかSQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するか
 
入社1年目のプログラミング初心者がSpringを学ぶための手引き
入社1年目のプログラミング初心者がSpringを学ぶための手引き入社1年目のプログラミング初心者がSpringを学ぶための手引き
入社1年目のプログラミング初心者がSpringを学ぶための手引き
 
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
初めてでも30分で分かるSpring 5 & Spring Boot 2オーバービュー
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門
 
REST API のコツ
REST API のコツREST API のコツ
REST API のコツ
 
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐ
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
Spring Bootをはじめる時にやるべき10のこと
Spring Bootをはじめる時にやるべき10のことSpring Bootをはじめる時にやるべき10のこと
Spring Bootをはじめる時にやるべき10のこと
 
データベース設計徹底指南
データベース設計徹底指南データベース設計徹底指南
データベース設計徹底指南
 
今さら聞けないDiとspring
今さら聞けないDiとspring今さら聞けないDiとspring
今さら聞けないDiとspring
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
 
ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?
 
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
 
Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
 

Similaire à 社内Java8勉強会 ラムダ式とストリームAPI

3月度定例会プレゼン資料 張田浩明 最新
3月度定例会プレゼン資料 張田浩明 最新3月度定例会プレゼン資料 張田浩明 最新
3月度定例会プレゼン資料 張田浩明 最新hharita
 
システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4shingo suzuki
 
システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4shingo suzuki
 
Javaクラスファイルの読み方
Javaクラスファイルの読み方Javaクラスファイルの読み方
Javaクラスファイルの読み方y torazuka
 
「愛されたい!」と思ったときにJavaで書くRubyクラス
「愛されたい!」と思ったときにJavaで書くRubyクラス「愛されたい!」と思ったときにJavaで書くRubyクラス
「愛されたい!」と思ったときにJavaで書くRubyクラスKoichiro Ohba
 
Ruby on Rails 4.0 勉強会資料
Ruby on Rails 4.0 勉強会資料Ruby on Rails 4.0 勉強会資料
Ruby on Rails 4.0 勉強会資料techscore
 
第37回NDS Java並行処理 今昔物語
第37回NDS Java並行処理 今昔物語第37回NDS Java並行処理 今昔物語
第37回NDS Java並行処理 今昔物語civic Sasaki
 
Solr 4.0 の主な機能
Solr 4.0 の主な機能Solr 4.0 の主な機能
Solr 4.0 の主な機能Shinichiro Abe
 
Dark vol4 for_slideshare
Dark vol4 for_slideshareDark vol4 for_slideshare
Dark vol4 for_slideshareara_ta3
 
Vim scriptとJavaとHaskell
Vim scriptとJavaとHaskellVim scriptとJavaとHaskell
Vim scriptとJavaとHaskellaiya000
 
ラムダのコンパイル結果を5分で説明するよ​
ラムダのコンパイル結果を5分で説明するよ​ラムダのコンパイル結果を5分で説明するよ​
ラムダのコンパイル結果を5分で説明するよ​YujiSoftware
 
Java concurrency in_practice_chap06
Java concurrency in_practice_chap06Java concurrency in_practice_chap06
Java concurrency in_practice_chap06ohtsuchi
 
JVMの中身を可視化してみた
JVMの中身を可視化してみたJVMの中身を可視化してみた
JVMの中身を可視化してみたKengo Toda
 
【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~
【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~
【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~Developers Summit
 
プログラミングNet framework3のお題
プログラミングNet framework3のお題プログラミングNet framework3のお題
プログラミングNet framework3のお題Kazushi Kamegawa
 

Similaire à 社内Java8勉強会 ラムダ式とストリームAPI (17)

3月度定例会プレゼン資料 張田浩明 最新
3月度定例会プレゼン資料 張田浩明 最新3月度定例会プレゼン資料 張田浩明 最新
3月度定例会プレゼン資料 張田浩明 最新
 
システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4
 
システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4システムパフォーマンス勉強会#4
システムパフォーマンス勉強会#4
 
Java8でRDBMS作ったよ
Java8でRDBMS作ったよJava8でRDBMS作ったよ
Java8でRDBMS作ったよ
 
Javaクラスファイルの読み方
Javaクラスファイルの読み方Javaクラスファイルの読み方
Javaクラスファイルの読み方
 
「愛されたい!」と思ったときにJavaで書くRubyクラス
「愛されたい!」と思ったときにJavaで書くRubyクラス「愛されたい!」と思ったときにJavaで書くRubyクラス
「愛されたい!」と思ったときにJavaで書くRubyクラス
 
Ruby on Rails 4.0 勉強会資料
Ruby on Rails 4.0 勉強会資料Ruby on Rails 4.0 勉強会資料
Ruby on Rails 4.0 勉強会資料
 
第37回NDS Java並行処理 今昔物語
第37回NDS Java並行処理 今昔物語第37回NDS Java並行処理 今昔物語
第37回NDS Java並行処理 今昔物語
 
Solr 4.0 の主な機能
Solr 4.0 の主な機能Solr 4.0 の主な機能
Solr 4.0 の主な機能
 
Dark vol4 for_slideshare
Dark vol4 for_slideshareDark vol4 for_slideshare
Dark vol4 for_slideshare
 
Vim scriptとJavaとHaskell
Vim scriptとJavaとHaskellVim scriptとJavaとHaskell
Vim scriptとJavaとHaskell
 
ラムダのコンパイル結果を5分で説明するよ​
ラムダのコンパイル結果を5分で説明するよ​ラムダのコンパイル結果を5分で説明するよ​
ラムダのコンパイル結果を5分で説明するよ​
 
Java concurrency in_practice_chap06
Java concurrency in_practice_chap06Java concurrency in_practice_chap06
Java concurrency in_practice_chap06
 
Clojure
ClojureClojure
Clojure
 
JVMの中身を可視化してみた
JVMの中身を可視化してみたJVMの中身を可視化してみた
JVMの中身を可視化してみた
 
【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~
【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~
【デブサミ福岡B5】コードレビューの進め方~全員で行う品質の維持~
 
プログラミングNet framework3のお題
プログラミングNet framework3のお題プログラミングNet framework3のお題
プログラミングNet framework3のお題
 

Plus de Akihiro Ikezoe

Reactive Systems と Back Pressure
Reactive Systems と Back PressureReactive Systems と Back Pressure
Reactive Systems と Back PressureAkihiro Ikezoe
 
Incremental DOM and Recent Trend of Frontend Development
Incremental DOM and Recent Trend of Frontend DevelopmentIncremental DOM and Recent Trend of Frontend Development
Incremental DOM and Recent Trend of Frontend DevelopmentAkihiro Ikezoe
 
Embulkを活用したログ管理システム
Embulkを活用したログ管理システムEmbulkを活用したログ管理システム
Embulkを活用したログ管理システムAkihiro Ikezoe
 
RubyistのためのSilverlight2
RubyistのためのSilverlight2RubyistのためのSilverlight2
RubyistのためのSilverlight2Akihiro Ikezoe
 
Silverlight2でつくるリッチなTrac用UI
Silverlight2でつくるリッチなTrac用UISilverlight2でつくるリッチなTrac用UI
Silverlight2でつくるリッチなTrac用UIAkihiro Ikezoe
 

Plus de Akihiro Ikezoe (6)

Reactive Systems と Back Pressure
Reactive Systems と Back PressureReactive Systems と Back Pressure
Reactive Systems と Back Pressure
 
Incremental DOM and Recent Trend of Frontend Development
Incremental DOM and Recent Trend of Frontend DevelopmentIncremental DOM and Recent Trend of Frontend Development
Incremental DOM and Recent Trend of Frontend Development
 
Reactive
ReactiveReactive
Reactive
 
Embulkを活用したログ管理システム
Embulkを活用したログ管理システムEmbulkを活用したログ管理システム
Embulkを活用したログ管理システム
 
RubyistのためのSilverlight2
RubyistのためのSilverlight2RubyistのためのSilverlight2
RubyistのためのSilverlight2
 
Silverlight2でつくるリッチなTrac用UI
Silverlight2でつくるリッチなTrac用UISilverlight2でつくるリッチなTrac用UI
Silverlight2でつくるリッチなTrac用UI
 

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

  • 4. 4 / 54 目次 • 概要 • ラムダ式の基礎 • ストリームAPIの基礎 • ストリームAPIの拡張
  • 5. 5 / 54 ラムダ式とストリームAPI • ラムダ式とは関数を簡便に表現するための記法。 • ストリームAPIは、ラムダ式を利用したコレク ション操作用のAPI • 関数型プログラミング言語由来。歴史は古い。 • これまでの手続き型やオブジェクト指向的なプ ログラミング手法から、関数型プログラミング に変わります。 • パラダイムシフトのよかん!!
  • 6. 6 / 54 簡単なサンプル • フルーツの一覧の中から • 名前が“りんご”で始まり、 • 値段が100円以上のものを、 • 値段順で並び替え、 • 名前だけを取り出して、 • リストを作成する 1 List<String> apples = fruits.stream() 2 .filter(f -> f.getName().startsWith("りんご")) 3 .filter(f -> f.getPrice() > 100) 4 .sorted(Comparator.comparingInt(Fruit::getPrice)) 5 .map(Fruit::getName) 6 .collect(Collectors.toList());
  • 7. 7 / 54 メリット • 手続き的だった記述が宣言的になる • 保守性の向上…? • 可読性の向上…? • 簡単に並列実行できるようになる
  • 9. 9 / 54 可読性は? • 野球選手の一覧から、チームごとの投手の平均 年俸を取得する処理の例。 1 Map<String, Double> m = players.stream() 2 .collect(Collectors.groupingBy(player -> player.getTeam())) 3 .entrySet() 4 .stream() 5 .collect(Collectors.toMap( 6 entry -> entry.getKey(), 7 entry -> entry.getValue().stream() 8 .filter(player -> player.getPosition().equals("投手")) 9 .mapToInt(player -> player.getSalary()) 10 .average() 11 .orElse(0) 12 ) 13 ); • 気をつけないとすぐに読みにくくなる。 • stream()とかstream()とかstream()とかノイズが多い。
  • 12. 12 / 54 なぜラムダ式が必要になったのか • 非同期処理や並列処理が当たり前に使われるよ うになり、ラムダ式の必要性が高まった。 • Microsoftの提案を受け入れていれば、ラムダ 式がもっと早く入っていたかもしれない。 • でも、15年も先を見越して言語設計するなん てことは難しい。
  • 13. 13 / 54 ラムダ式 • 関数を第一級オブジェクトとして扱えるように なった。 • JVMで関数を直接扱えるようになったわけでは なく、内部的にはクラスのインスタンスを使っ て表現している。
  • 14. 14 / 54 ラムダ式の書き方 () -> 123 x -> x * x (x, y) -> x + y (int x, int y) -> { return x + y; } いろいろ省略できる! 仮引数や戻り値の型はほとんど 型推論してくれる。かしこい!
  • 15. 15 / 54 ラムダ式の使い方 • ラムダ式を渡される側 void hoge(Function<Integer, Integer> func) { Integer ret = func.apply(42); } • ラムダ式を渡す側 hoge(x -> x * x); 関数型インタフェース
  • 16. 16 / 54 関数型インタフェース • ラムダ式の型は関数型インタフェースで表現さ れる。 • 関数型インタフェースとは • 実装するべきメソッドを1つだけ持ってる interface • @FunctionalInterfaceアノテーションを付ける と、コンパイル時にメソッドが1つだけかどうか チェックしてくれる。つけなくてもよい。 @FunctionalInterface public interface Function<T, R> { R apply(T t); }
  • 17. 17 / 54 標準の関数型インタフェース • Runnable, Consumer, Function, Predicate,Supplier • BiConsumer,BiFunction,BiPredicate • BooleanSupplier • IntBinaryOperator,IntConsumer,IntFunction,IntPre dicate,IntSupplier,IntToDoubleFunction,IntToLong Function,IntUnaryOperator,ObjIntConsumer,ToIntBi Function,ToIntFunction • LongBinaryOperator,LongConsumer,LongFunction,Lon gPredicate,LongSupplier,LongToDoubleFunction,Lon gToIntFunction,LongUnaryOperator,ObjLongConsumer ,ToLongBiFunction,ToLongFunction • DoubleBinaryOperator,DoubleConsumer,DoubleFuncti on,DoublePredicate,DoubleSupplier,DoubleToIntFun ction,DoubleToLongFunction,DoubleUnaryOperator,O bjDoubleConsumer,ToDoubleBiFunction,ToDoubleFunc tion
  • 18. 18 / 54 代表的な関数型インタフェース • Runnable: 引数なし、戻り値なし • Consumer: 引数1つ、戻り値なし • Function: 引数1つ、戻り値あり • Predicate: 引数1つ、戻り値がboolean • Supplier: 引数なし、戻り値あり • Bi + Xxxxx: 引数が2つ
  • 19. 19 / 54 無名内部クラスとラムダ式の違い 無名内部クラス ラムダ式 外部変数へのアクセス 実質的なfinalの変 数のみアクセス可能。 実質的なfinalの変数のみ アクセス可能。 エンクロージングイン スタンスの参照 必ず持っている。 必要がなければ持たない。 メモリリークがおきにくい。 クラスファイルの生成 コンパイル時にクラ スが生成される。 実行時にクラスが生成され る。 クラスのロード時間が短縮 されるかも。 インスタンスの生成 明示的にnewする。 JVMが最適な生成方法を選 択する。
  • 20. 20 / 54 Javaのラムダ式はクロージャではない • ローカル変数は、実質的にfinalな変数にしか アクセスできない。 • 独自のスコープは持たず、外のスコープを引き 継ぐ。 • エンクロージングインスタンスの参照は、基本 持たない。
  • 21. 21 / 54 ラムダ式のスコープ class Outer { public void func() { final int a = 0; int b = 1; list.stream().forEach(x -> { int a = 2; System.out.println(b); }); b = 3; } } 値の変更が行われるローカル 変数はアクセス不可 独自のスコープを持たな いので、変数名の衝突が 起きる。 明示しなければ、エンクロー ジングインスタンスの参照を 持たない。
  • 22. 22 / 54 例外は苦手かも • ラムダ式からチェック例外のあるAPIを呼び出す 場合 • 関数型インタフェースの定義にthrowsを記述する (標準の関数型インタフェースにはついてない) • ラムダ式の中でtry-catchを書く list.map(x -> { try { // チェック例外のある呼び出し } catch(XxxException ex) { // エラー処理。 } }).collect(Collectors.toList());
  • 23. 23 / 54 メソッド参照 • ラムダ式だけでなく、既存のメソッドも関数型 インタフェースで受け取ることが可能。 // ラムダ式を使った場合 list.forEach(x -> System.out.println(x)); // メソッド参照を使った場合 list.forEach(System.out::println); fruits.map(fruit -> fruit.getName()); // インスタンスメソッドの参照もOK fruits.map(Fruit::getName);
  • 24. 24 / 54 Lambda Expression Deep Dive • ラムダ式は無名内部クラスのシンタックスシュガーじゃな い。 • コンパイル時ではなく実行時にクラスが生成される。 • invokeDynamic命令を使っている。 • ラムダ式をコンパイルするとどんなバイトコードが生成さ れるかみてみよう。 対象のコードはこんな感じ public class Main { public void main(){ Sample sample = new Sample(); sample.func(x -> x * x); } }
  • 25. 25 / 54 ラムダ式のバイトコード INVOKEDYNAMIC apply()Ljava/util/function/IntFunction; [ // handle kind 0x6 : INVOKESTATIC java/lang/invoke/LambdaMetafactory.metafactory() // arguments: (I)Ljava/lang/Object;.class, // handle kind 0x6 : INVOKESTATIC Main.lambda$main$0((I)Ljava/lang/Integer;) , (I)Ljava/lang/Integer;.class ] BIPUSH 12 INVOKEVIRTUAL Sample.func (Ljava/util/function/IntFunction;I)I // ・・・途中省略・・・ private static lambda$main$0(I)Ljava/lang/Integer; L0 // ラムダ式の中の処理 x -> x * x ラムダのオブジェクトをスタックに積んで メソッドを呼び出す ラムダ式の オブジェクト をつくる命令 ラムダ式の なかみ
  • 26. 26 / 54 ラムダ式の実行時の挙動 Main.classMain.class コンパイル時に 生成されるもの invoke dynamicinvoke dynamic static method lambda$main$0 static method lambda$main$0 LambdaMetafactory #metafactory LambdaMetafactory #metafactory ラムダのインス タンスをつくる メソッド ラムダのインス タンスをつくる メソッド class Lambda$1 関数型インタフェー スを実装 lambda$main$0 を呼び出す class Lambda$1 関数型インタフェー スを実装 lambda$main$0 を呼び出す JVMの中のクラス 実行時に作られるもの 1回目の呼び出し (ブートストラップ) 2回目以降 の呼び出し (Method Handle) 作成 Mainの内部クラス として作成 new
  • 27. 27 / 54 なぜこんな複雑なことを? • コンパイル時にクラスを大量につくると、クラ スロードのときに遅くなるかもしれないから。 とくにストリームAPIではラムダ式を大量につ かうので。 • invokeDynamicを使うとパフォーマンスをあ まり落とさず動的な処理が実現できる。 • 今後JVMの実装が変わると、インスタンスの生 成方法がより効率的なものになるかも。
  • 29. 29 / 54 ストリームAPIとは • パイプライン型のデータ処理のAPI • 絞り込み、データ変換、グループ化、集計など の操作をそれぞれ分離した形で記述できる。 • 絞り込みの条件や、加工方法などをラムダ式で 指定する。 • メソッドチェイン形式で記述できる。
  • 30. 30 / 54 ストリームパイプライン • ストリームパイプラインの構成要素 • ソース(Source) • 中間操作(Intermediate Operation) • 終端操作(Terminal Operation) • ストリームパイプラインは、1つ以上のソース、 0個以上の中間操作、1つの終端操作から構成 される。 • 1つのStreamに対して複数の終端操作(いわゆ る分配)をおこなってはいけない。 • 終端操作の結果をソースとして処理を継続する ことも可能。
  • 31. 31 / 54 サンプル • 1行目がソース • 2行目から5行目までが中間操作 • 6行目が終端操作 1 List<String> apples = fruits.stream() 2 .filter(f -> f.getName().startsWith("りんご")) 3 .filter(f -> f.getPrice() > 100) 4 .sorted(Comparator.comparingInt(Fruit::getPrice)) 5 .map(Fruit::getName) 6 .collect(Collectors.toList());
  • 33. 33 / 54 ソース • 既存のデータからStream型のオブジェクトを つくる。 • Streamの種類 • Stream<T> • IntStream, LongStream, DoubleStream • つくりかた • Collection#stream • Arrays#stream • Stream#of • BufferReader#lines • IntStream#range
  • 34. 34 / 54 ソースの特性 • Sequential, Parallel • 逐次実行か、並列実行か。 • Stream#parallelとStream#sequentialで相互に 変換可能。 • Ordered, Unordered • Listや配列などはOrdered, SetなどはUnordered • Stream#unorderedで順序を保証しないStreamに 変換可能。 • 無限リスト
  • 35. 35 / 54 中間操作 • 絞り込みや写像などの操作を指定して、新しい Streamを返す。 • 遅延実行 • 処理方法を指定するだけで、実際には処理しない。 • 中間操作を呼び出すたびにループしてたら効率が悪い。 終端操作が呼ばれたときに、複数の中間操作をまとめて ループ処理。 • 処理の種類 • 絞り込み: filter • 写像: map, flatMap • 並び替え: sorted • 数の制御: limit, skip • 同一要素除外: distinct • tee的なもの: peek
  • 36. 36 / 54 特殊な中間操作 • ステートフルな中間操作:distinct, sorted • 中間操作は基本的にステートレスだが、ステートフ ルなものもある。 • 無限リストや並列処理のときに注意が必要。 • ショートサーキット評価な中間操作:limit • 指定された数の要素を流したら処理を打ち切る。 • 無限Streamを有限Streamにしてくれる。 • 副作用向け中間操作:peek • 中間操作では基本的に副作用するべきでない。 • デバッグやログ出力用途以外にはなるべく使わない ようにしよう。
  • 37. 37 / 54 終端操作 • ストリームパイプラインを実行して、なんらか の結果を取得する処理。 forEachだけは戻り値を返さない。副作用専用 のメソッド。 • 処理の種類 • たたみ込み:collect, reduce • 集計:min, max, average, sum, count • 単一の値の取得:findFirst, findAny • 条件:allMatch, anyMatch, noneMatch • 繰り返し:forEach, forEachOrdered
  • 38. 38 / 54 汎用的な終端操作:collect • 引数にCollectorを指定して様々な処理がおこなえる。 • 集計: • averagingInt, averagingLong, averagingDouble • summingInt, summingLong, summingDouble • counting • maxBy, minBy • summarizing • グループ化 • groupingBy, partitioningBy • コンテナに累積 • toList, toMap, toSet • 結合 • joining • たたみ込み • reducing
  • 39. 39 / 54 Optionalを返す終端操作 • Optional • nullチェックめんどくさい・・・。パイプラインの途 中でnullが現れると流れが止まってしまう。 • nullを駆逐し(ry • Java8ではOptional型が入った。 • Stream APIの中にはOptionalを返す終端操作 がある。 • min, max, average, sum, findFirst, findAny, reduceなど。 • 空のリストに対してこれらの処理を呼び出すと Optional.emptyを返す。
  • 40. 40 / 54 ショートサーキット評価の終端操作 • ショートサーキット評価をする終端操作 • anyMatch, allMatch, noneMatch, findFirst, findAny • 例えば、Stream#countは全要素をループで回 すので時間がかかるが、Stream#findAnyは ショートサーキット評価なので、1つめの要素 が見つかればすぐに終わる。 if(stream.count() != 0) if(stream.findAny().isPresent())
  • 41. 41 / 54 並列処理 • すごく簡単に並列化できる。ソースを生成するときに、 Collection#parallelStreamや, Stream#parallel を使うだけ。 • 順序保証 • ソースがORDEREDであれば並列実行しても順番は保証される。 • ただし、順序を保つために内部にバッファリングするので、 あまりパフォーマンスはよくない。 • 順番を気にしないのであれば、unorderedすると効率がよく なる。 • 副作用に注意 • 中間操作で副作用が生じる場合はちゃんとロックしよう。 • ただし、ロックすると並列で実行しても待ちが多くなるので、 パフォーマンスはよくない。 • スレッドセーフでないArrayListなども並列実行可能。
  • 42. 42 / 54 並列処理のやりかた • parallel()をつけるだけ! • ただし、上記のような例ではあまり並列処理の うまみはない。 1 List<String> apples = fruits.stream().parallel() 2 .filter(f -> f.getName().startsWith("りんご")) 3 .filter(f -> f.getPrice() > 100) 4 .sorted(Comparator.comparingInt(Fruit::getPrice)) 5 .map(Fruit::getName) 6 .collect(Collectors.toList());
  • 43. 43 / 54 並列処理の挙動 中間 操作 中間 操作 中間 操作 結果 43 分割したソースごとに 異なるスレッドで中間 操作を並列に実行 ソースを複数に分割 spliterator 順序を保証する 場合は内部で バッファリング 中間 操作 中間 操作 中間 操作 複数スレッドの 実行結果を結合終端操作
  • 45. 45 / 54 ストリームAPIは機能不足? • なんかいろいろ足りない! • 他の言語と比べて標準で用意されてる機能が少ない。 • 複数のStreamを合成するzipすらないなんて… • 毎回stream()を呼ぶのめんどくさい… • 足りないならつくればいいじゃない。 • 関数型言語なら関数をどんどん増やせばよい。 C#なら拡張メソッドという仕組みがある。 • でも、JavaではStreamに自由にメソッドを増やせ ない!こまった!
  • 46. 46 / 54 ストリームAPIの拡張ポイント • たたみ込みを使えばたいていの処理はつくれる。 • 汎用的なCollectorをつくって、再利用できる ようにしておくとよさそう。 • CollectorをつくるためのヘルパーAPIが用意 されている。既存のCollectorを組み合わせた り、一から新しい終端操作をつくったりするこ とができる。
  • 47. 47 / 54 Collectorの構成要素 • supplier • コンテナの初期値を生成する人 • accumulator • 値を加工してコンテナに格納する人 • combiner • 並列で実行された場合、コンテナを結合する人 • finisher • 最後にコンテナを加工する人
  • 48. 48 / 54 Collectorの動き コンテナコンテナ コンテナコンテナ コンテナコンテナ 結果結果 supplier supplier combiner finisher accumulator accumulator 並列的にデータがやってくる
  • 49. 49 / 54 Collectorのつくり方 • Collector.of • supplier, accumulator, combiner, finisher を指定して新しいCollectorをつくる • Collectors.mapMerger • Collectorにcombinerを追加する • Collectors.mapping • accumulatorの前に実行される写像処理を追加す る • Collectors.collectingAndThen • Collectorにfinisherを追加する
  • 50. 50 / 54 Collectorを使った例 • チームごとの投手の平均年俸を取得する • 2つのCollectorを用意 • グループ化したストリームを返すCollector • グループごとの平均値を返すCollector 1 Map<String, Double> averageSalaryMap = players.stream() 2 .filter(player -> player.getPosition().equals("投手")) 3 .collect(groupedStreamCollector(Player::getTeam)) 4 .collect(averagePerGroupCollector(Player::getSalary));
  • 51. 51 / 54 Collectorの実装 1 static <T, V> Collector<Entry<V, List<T>>, ?, Map<V, Double>> 2 averagePerGroupCollector(ToIntFunction<T> mapper) { 3 return Collector.of( 4 () -> new HashMap<>(), 5 (map, entry) -> { 6 entry.getValue().stream() 7 .mapToInt(mapper) 8 .average() 9 .ifPresent(ave -> map.put(entry.getKey(), ave)); 10 }, 11 (left, right) -> { 12 left.putAll(right); 13 return left; 14 } 15 ); 16 } 17 static <T, K> Collector<T, ?, Stream<Entry<K, List<T>>>> 18 groupedStreamCollector(Function<T, ? extends K> mapper) { 19 return Collectors.collectingAndThen( 20 Collectors.groupingBy(mapper), map -> map.entrySet().stream()); 21 } accumulator combiner finisher • つくるのは難しいけど、汎用化しておけばいろ いろなところで使える。 supplier
  • 52. 52 / 54 まとめ • ラムダ式 • 関数を簡便に表記するための記法。 • デメリットも少ないし使わない手はないよね。 • ストリームAPI • 慣れるまでは読み書きも難しいし、変なバグを生み やすいかもしれない。 • でも慣れると少ない記述で複雑な処理が実現できる。 そして何より書いていて楽しい! • しかし、標準で用意されてる機能が少ないのに、拡 張が難しいのはどうにかならんかね? • 今後はオレオレコレクションやStreamUtilsクラ スが乱立する可能性も!?
  • 53. 53 / 54 おまけ • Java8を使うときはIntelliJ IDEAがおすすめ。 (EclipseのJava8正式対応は2014年6月) • Java8の文法にもしっかり対応(たまーに型推 論間違えたりするけど) • 無名内部クラスを書いてると、ラムダ式に書き 換えてくれる。 • for文を書いてると、ストリームAPIに書き換 えてくれる。(複雑な処理はムリだけど)
  • 54. 54 / 54 参考 • JavaのLambdaの裏事情 • http://www.slideshare.net/nowokay/java-2898601 • ラムダと invokedynamic の蜜月 • http://www.slideshare.net/miyakawataku/lambda-meetsinvokedynamic • 倭マン's BLOG • http://waman.hatenablog.com/category/Java8 • ラムダ禁止について本気出して考えてみた - 9つのパターンで見る Stream API • http://acro-engineer.hatenablog.com/entry/2013/12/16/235900 • Collectorを征す者はStream APIを征す(部分的に) • http://blog.exoego.net/2013/12/control-collector-to-rule-stream- api.html • きしだのはてな • http://d.hatena.ne.jp/nowokay/searchdiary?word=%2A%5Bjava8%5D • 徹底解説!Project Lambdaのすべて returns • http://www.slideshare.net/bitter_fox/java8-launchJava • SE 8 lambdaで変わるプログラミングスタイル • http://pt.slideshare.net/nowokay/lambdajava-se-8-lambda