面倒くさい説明を少々しますので飛ばす人はこちらへ→スキップ
StreamはC#でいうIEnumbleに準ずるもので、中間処理を実行しても実際の絞り込みは
実行されず、終端処理を呼び出された際に実行されるというものです。
下の例のような記述ができます。
int sumOfRedWeights = blocks.stream().filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();
ここでは、
blocksをgetColor()==REDで絞り込み(filter)、
getWeight()を抽出して(mapToInt)合計しています(sum)。
中間処理のfilter、mapToIntでは絞込は行われず、
終端処理のsumが呼ばれる際に遅延的に実行されます。
言い換えると、繰り返し処理を関数的に記述できるようになるというところでしょうか。
また、終端処理まで状態を保持しないため並列化にも適しています。
stream()のかわりにparallelStream()を使うことによって、処理を並列的に実行します。
サーバではもともとマルチスレッドなので他のコアも処理してたりして、
あまり恩恵ないかもしれないですが、可読性と並列化が捗りますね\(^o^)/。
ということで、
StreamとSQLのクエリと比較してみましょう。
単純なSELECTでTestMember.idのみを抽出する
//SQLSELECT TestMember.id
//Listに変換
List
.collect(Collectors.toList());
//Setに変換
Set
.collect(Collectors.toCollection(TreeSet::new));
TestMember::getIdは関数渡しでメソッドを渡しています。
map中でgetIdを呼び出した値を利用しています。
Member.weightの合計を求める
//SQLSELECT sum(TestMember.weight)
total = members.stream()
.mapToInt(x -> x.getWeight())
.sum();
合計を求める方法には、Collectorを使う方法もあります
int total = members.stream()
.collect(Collectors.summingInt(x -> x.getWeight()));
平均を求める
//SQLSELECT avg(TestMember.weight)
double average = members.stream()
.mapToInt(x -> x.getWeight())
.average()
.orElse(0d);
orElse()というメソッドが出てきました。
average()は、OptionalDoubleというNull許容なオブジェクトを返します。
OptionalDoubleのNullの場合のデフォルト値を指定してdoubleを返すのがorElse()です。
OptionalDoubleは、C#でいうところのdouble?ですね。
切り出し
何番目の要素を指定して取り出すということもできます。//SQL
SELECT TestMember.weight
LIMIT 1
members.stream()
.mapToInt(x -> x.getWeight())
.findFirst()
.orElse(0);
//SQL
SELECT * OFFSET 1 LIMIT 2
members.stream()
.skip(1)
.limit(2).toArray();
グループ・パーティション
Stream には group byや partitioned byに当たるメソッドもあります//グループ分け
Map
.collect(Collectors.groupingBy(x -> x.getPart()));
同じpartのTestMemberをpart毎のマップにする。
本当は、groupingByには関数渡しで、TestMember::getPartとできるはずが、
エラーになっちゃいますね。
//パーティション
Map
.collect(Collectors.partitioningBy(s -> s.getId() >= 5));
その他
条件に一致したものが一件でもあるboolean match = members.stream().anyMatch(b -> b.getId() > 5);
countを使うより軽いはず?
weightでソートする
ソートをstream、ラムダ式で実装してみた
members.stream().sorted((r,l) -> {
return Integer.compare(r.weight, l.weight);
}).collect(Collectors.toList());
ソートするだけならもっと効率のいい方法があるかな
parallelStreamが使えるから早いのか?むむ?
今回使ったプロジェクト->StreamSample.zip