IntStreamで遊んでみた
暇だったので、Java 8から追加されたjava.util.stream.IntStream
でちょっと遊んでみた。
ちなみに、IntStream
はinterface
であるが、ソースを見るとstaticメソッドの実装が書いてある。IntStream#of
とか。まぁそれは余談。
以下のようなソースで出力を見ていく。
import java.util.Arrays; import java.util.stream.IntStream; public class Main { public static void main(String[] args) { int[] ary = { 5, 2, 8, 2, 9, 1, 3, 0, 5, 6, 4, 7, }; System.out.println("--------countTest"); countTest(ary); System.out.println("--------sumTest"); sumTest(ary); System.out.println("--------maxTest"); maxTest(ary); System.out.println("--------minTest"); minTest(ary); System.out.println("--------sortTest"); sortTest(ary); System.out.println("--------1つのstreamで連続処理できるのかテスト"); test(ary); System.out.println("--------パフォーマンスを計ってみる(sumを例に)"); for (int ct = 1; ct <= 100000000; ct *= 10) { performance(ary, ct); } System.out.println("--------その他"); othersTest(ary); System.out.println("--------classTest"); classTest(ary); } private static void countTest(int[] ary) { // 配列が手元にある場合に最もポピュラーな手段 System.out.println("count1=" + ary.length); // Streamしか手元にない場合は仕方ない IntStream stream = IntStream.of(ary); System.out.println("count2=" + stream.count()); } private static void sumTest(int[] ary) { // ループ変数を使って要素にアクセス int sum1 = 0; for (int i = 0; i < ary.length; ++i) { sum1 += ary[i]; } System.out.println("sum1=" + sum1); // 拡張for文 int sum2 = 0; for (int a : ary) { sum2 += a; } System.out.println("sum2=" + sum2); // java.util.Arrays.stream() System.out.println("sum3=" + Arrays.stream(ary).sum()); // java.util.stream.IntStream System.out.println("sum4=" + IntStream.of(ary).sum()); } private static void maxTest(int[] ary) { // ループ変数を使って要素にアクセス int max1 = Integer.MIN_VALUE; for (int i = 0; i < ary.length; ++i) { max1 = Math.max(max1, ary[i]); } System.out.println("max1=" + max1); // 拡張for文 int max2 = Integer.MIN_VALUE; for (int a : ary) { max2 = Math.max(max2, a); } System.out.println("max2=" + max2); // java.util.Arrays.stream() System.out.println("max3=" + Arrays.stream(ary).max()); // java.util.stream.IntStream System.out.println("max4=" + IntStream.of(ary).max()); } private static void minTest(int[] ary) { // ループ変数を使って要素にアクセス int min1 = Integer.MAX_VALUE; for (int i = 0; i < ary.length; ++i) { min1 = Math.min(min1, ary[i]); } System.out.println("min1=" + min1); // 拡張for文 int min2 = Integer.MAX_VALUE; for (int a : ary) { min2 = Math.min(min2, a); } System.out.println("min2=" + min2); // java.util.Arrays.stream() System.out.println("min3=" + Arrays.stream(ary).min()); // java.util.stream.IntStream System.out.println("min4=" + IntStream.of(ary).min()); } private static void sortTest(int[] ary) { // java.util.Arraysのsortメソッド(副作用あり) int[] ary1 = ary.clone(); Arrays.sort(ary1); System.out.println("sort1=" + Arrays.toString(ary1)); // java.util.stream.IntStreamのソート済みstreamを取得するsortedメソッド(副作用無し) IntStream stream2 = IntStream.of(ary).sorted(); System.out.println("sort2=" + Arrays.toString(stream2.toArray())); System.out.println("orig =" + Arrays.toString(ary)); } private static void test(int[] ary) { try { IntStream stream = IntStream.of(ary); System.out.println("sumT=" + stream.sum()); System.out.println("maxT=" + stream.max()); System.out.println("minT=" + stream.min()); System.out.println("sortT=" + Arrays.toString(stream.sorted().toArray())); } catch (RuntimeException e) { e.printStackTrace(); } } private static void performance(int[] ary, int loopCount) { // ループ変数を使って要素にアクセス long S1 = System.currentTimeMillis(); for (int ct = 0; ct < loopCount; ++ct) { int sum1 = 0; for (int i = 0; i < ary.length; ++i) { sum1 += ary[i]; } } long G1 = System.currentTimeMillis(); // 拡張for文 long S2 = System.currentTimeMillis(); for (int ct = 0; ct < loopCount; ++ct) { int sum2 = 0; for (int a : ary) { sum2 += a; } } long G2 = System.currentTimeMillis(); // java.util.Arrays.stream() long S3 = System.currentTimeMillis(); for (int ct = 0; ct < loopCount; ++ct) { int sum3 = Arrays.stream(ary).sum(); } long G3 = System.currentTimeMillis(); // java.util.stream.IntStream long S4 = System.currentTimeMillis(); for (int ct = 0; ct < loopCount; ++ct) { int sum4 = IntStream.of(ary).sum(); } long G4 = System.currentTimeMillis(); System.out.printf("sum(ms) %16dct | %5d | %5d | %5d | %5d |%n", loopCount, G1 - S1, G2 - S2, G3 - S3, G4 - S4); } private static void othersTest(int[] ary) { System.out.println("orig =" + Arrays.toString(ary)); // distinct IntStream stream1 = IntStream.of(ary).distinct(); System.out.println("distinct=" + Arrays.toString(stream1.toArray())); // skip IntStream stream2 = IntStream.of(ary).skip(1); System.out.println("skip =" + Arrays.toString(stream2.toArray())); // limit IntStream stream3 = IntStream.of(ary).limit(5); System.out.println("limit =" + Arrays.toString(stream3.toArray())); } private static void classTest(int[] ary) { IntStream stream; stream = IntStream.empty(); System.out.println("empty =" + stream.getClass().getName()); stream = IntStream.range(1, 2); System.out.println("range =" + stream.getClass().getName()); stream = IntStream.concat(IntStream.empty(), IntStream.empty()); System.out.println("concat =" + stream.getClass().getName()); stream = IntStream.of(ary); System.out.println("of =" + stream.getClass().getName()); stream = IntStream.of(ary).sorted(); System.out.println("sorted =" + stream.getClass().getName()); stream = IntStream.of(ary).filter(i -> true); System.out.println("filter =" + stream.getClass().getName()); stream = IntStream.of(ary).map(i -> i); System.out.println("map =" + stream.getClass().getName()); stream = IntStream.of(ary).distinct(); System.out.println("distinct =" + stream.getClass().getName()); stream = IntStream.of(ary).skip(1); System.out.println("skip =" + stream.getClass().getName()); stream = IntStream.of(ary).limit(1); System.out.println("limit =" + stream.getClass().getName()); } }
各々の出力結果
countTest
--------countTest count1=12 count2=12
int配列が手元にあるなら要らないだろうというメソッドcount()
。
Streamしか手元にないなら仕方ないだろうが、Streamの要素を数え上げるという処理によって要素が消費されてその後は何もできなくなる(後述)ので、要素数を取得してStreamを捨ててもよいという場合にしか使えない。
sumTest
--------sumTest sum1=52 sum2=52 sum3=52 sum4=52
これは、配列の要素のsumを計算するのに1行で書けるのでちょっとうれしいかも。
ちなみに、IntStream.of(int...)
は内部でArrays.stream(int[])
を呼んでいる。
maxTest
--------maxTest max1=9 max2=9 max3=OptionalInt[9] max4=OptionalInt[9]
これは、OptionalIntが返ってくるのがちょっと微妙なところ。max値がInteger.MIN_VALUEかどうかと比較するのとどっちがいいかという感じ。
minTest
--------minTest min1=0 min2=0 min3=OptionalInt[0] min4=OptionalInt[0]
maxと同じく。
sortTest
--------sortTest sort1=[0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9] sort2=[0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9] orig =[5, 2, 8, 2, 9, 1, 3, 0, 5, 6, 4, 7]
元の配列への副作用を発生させないために、ary.clone()
するかIntStream#sorted
を使うかという感じ。ソートされたStreamを取得して更にあれこれやりたい場合には有用。
test
--------1つのstreamで連続処理できるのかテスト sumT=52 java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229) at java.util.stream.IntPipeline.reduce(IntPipeline.java:461) at java.util.stream.IntPipeline.max(IntPipeline.java:424) at Main.test(Main.java:111) at Main.main(Main.java:19)
これはまぁ予想通り。java.io.InputStreamやjava.io.Readerからの読み込みを(一部を除いて)巻き戻し再実行できないのと同じ。
(補足)sum、max、min、average、countが欲しいだけならsummaryStatistics()
メソッドを使えばいいらしい。
performance
--------パフォーマンスを計ってみる(sumを例に) sum(ms) 1ct | 0 | 0 | 0 | 0 | sum(ms) 10ct | 0 | 0 | 0 | 0 | sum(ms) 100ct | 0 | 0 | 1 | 0 | sum(ms) 1000ct | 0 | 0 | 9 | 6 | sum(ms) 10000ct | 2 | 2 | 4 | 16 | sum(ms) 100000ct | 5 | 28 | 8 | 18 | sum(ms) 1000000ct | 75 | 66 | 102 | 53 | sum(ms) 10000000ct | 0 | 0 | 649 | 607 | sum(ms) 100000000ct | 0 | 0 | 6118 | 6154 |
まぁ、IntStreamオブジェクトを生成するから仕方ない。
othersTest
--------その他 orig =[5, 2, 8, 2, 9, 1, 3, 0, 5, 6, 4, 7] distinct=[5, 2, 8, 9, 1, 3, 0, 6, 4, 7] skip =[2, 8, 2, 9, 1, 3, 0, 5, 6, 4, 7] limit =[5, 2, 8, 2, 9]
distinctは重複要素を除去した新たなStreamを生成する。
skipは先頭の指定された要素数をスキップした新たなStreamを生成する。
limitは先頭の指定された要素数だけを返す新たなStreamを生成する。
その他にfilterとかmapとか色々あるが割愛(謎)。lambda式から逃げてるだけとも言わなくもない(ぉ‥
classTest
--------classTest empty =java.util.stream.IntPipeline$Head range =java.util.stream.IntPipeline$Head concat =java.util.stream.IntPipeline$Head of =java.util.stream.IntPipeline$Head sorted =java.util.stream.SortedOps$OfInt filter =java.util.stream.IntPipeline$9 map =java.util.stream.IntPipeline$3 distinct =java.util.stream.ReferencePipeline$4 skip =java.util.stream.SliceOps$2 limit =java.util.stream.SliceOps$2
java.util.stream.IntStream
がインタフェースだということで、実装クラスがどうなっているのか気になったので列挙してみた。