続きです
sumを使ったList内部の計算
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
System.out.println(intList);
Long count = intList.stream().mapToLong(value -> Integer.toUnsignedLong(value)).sum();
System.out.println(count);
最初のまとめでは説明を省略しましたが、末端処理のsumを使ってList内部の計算になります。
sumはIntStream LongStream DoubleStream
でのみ呼び出し可能な末端処理となっていますので、中間処理完了時点で型を合わせてあげる必要がある点に注意が必要です。
mapToXXX
はそのような形に変換してくれるので、今回はそれを使いました。
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
System.out.println(intList);
Long count = intList.stream().map(value -> value).mapToLong(value -> Long.valueOf(value)).sum();
System.out.println(count);
mapは中間処理なので、複数繋げて何回もリストの変換を行うことも可能なので、ほとんどのケースはこれでいけそうな感じがします。
List内部の要素を取得
前回はmatchする形での取得を書きましたが、今回はListの最初の要素など決まった要素を取得する形について書きます。
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
System.out.println(intList);
Integer count = intList.stream().findFirst().get();
System.out.println(count);
List内の最初の要素を取得する場合は末端処理のfindFirst
を使用します。
見つかった最初の要素を取得することが出来ます。
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
System.out.println(intList);
Integer count = intList.stream().findAny().get();
System.out.println(count);
List内のランダム要素を取得する場合は、findAny
を使えば取れるらしいのですが、実行すると最初しか取れなかったのでちょっと良く分からないなーというしかありません。
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
System.out.println(intList);
Integer count = intList.stream().reduce((base, value) -> value).get();
System.out.println(count);
また、findLast
はないので、Listの最後の要素を取りたいという場合にはfindは出来ません(なんてこったい)。
色々やり方を調べてみたのですが、reduce
を使って、現在の値を返却し続けるというのが一番楽に実装出来そうです(300万件のListから最後のリストを取得するという処理で性能をはかりましたが、100msもかからなかったので性能的にも問題なさそうです)
Streamで色々処理した後の個数を取得する
いわゆるcountを計測する方法です。
変換後の値も使用したい場合は、中間処理を終わった段階のStream
を保持して、末端処理をいくつも書くのが良いと思います。
List<String> stringList = new ArrayList<String>();
stringList.add("japan");
stringList.add("usa");
stringList.add("england");
int count = stringList.stream().collect(Collectors.toList()).size();
System.out.println(count);
collectしてからsize
で取り出しても良いですが
List<String> stringList = new ArrayList<String>();
stringList.add("japan");
stringList.add("usa");
stringList.add("england");
int count = stringList.stream().count();
System.out.println(count);
末端処理としてcount
が定義されているので、これを使うと簡単に要素数が取得出来ます。
From Map to List
Map<String, String> stringMap = new HashMap<String, String>();
stringMap.put("japan", "japanese");
stringMap.put("usa", "english");
stringMap.put("england", "english");
List<String> list = stringMap.entrySet().stream().map(entry -> entry.getKey()).collect(Collectors.toList());
System.out.println(list);
Map<K,V>
からList<T>
への変換は簡単です。entry
のstream
を取得してmap
するだけです。その際の引数はentry
になるので、そこだけ注意が必要です。
たまに(key, value)
とかやってはまるバカがここにいます。
拡張for文ではなく普通のfor文を変換する
for(int i = 0; i < 5; i++) {
System.out.println(i);
}
よくある数値を使ってのループもラムダ(正確にはStream)を使うと変換することが出来ます。
Stream.iterate(0, i -> i + 1).limit(5).forEach(System.out::println);
Stream
のClass内にiterate
というstaticメソッドがあり、それを使うことで実現可能になります。
このメソッドは<T>
型の無限のシーケンシャルStreamを作ってくれます。
第一引数に初期値(seed)(position 0の要素)が入ります。
第二引数は(無限に)繰り返して呼ばれ、returnした値が呼ばれた順番の要素になります。Lambdaでの引数は1個前の要素が入ってきます。
上の例ではHaskellの[0..4]
と同様の結果を得る事が出来るStreamとなっています。
無限なので、作った後にきちんと使う所だけを抽出してあげないといけないのですが、filter
ではうまく上の例を表現できなかったので、現状ではlimit
を使って個数を制限した方が良いという結論です。他の中間処理をさせたい場合も先にlimit
してからの方が効率的だと思います。
最後に
便利なstreamといえども、出来ない事もあるので、きちんと使えるケースを把握しておかないとたまにはまります。
そうなると勿体ないので、しっかりケースを把握していければ強力なstreamが味方になってくれて助かるのかと思ってます。
まだ新しく書くことがどんどん出てきている状況ですが、その3作るのも面倒なのである程度はここに追記されていきます。