Java の Stream インターフェースに定義されているメソッドを見ていくシリーズ(目次)。 今回は Stream インターフェースに定義されている static メソッドを見ていきます。 というか、Java 8 からインターフェースに static メソッドが定義できるようになったんですな。 Stream インターフェースに定義されている static メソッドはどれも Stream オブジェクトを生成する static ファクトリメソッドです。 なんか、このシリーズ、「Reduce 処理 → Map 処理 → Stream の生成」と、記事の順序がおもいっきり逆な気がしますが・・・ 今更ですな。 まぁそれはおいといて、今回扱うメソッドは以下の通り:
static <T> Stream<T> empty() static <T> Stream<T> of(T t) static <T> Stream<T> of(T... values) static <T> Stream<T> generate(Supplier<T> s) static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) static <T> StreamBuilder<T> builder()
Stream オブジェクトを生成する方法はこれらの方法以外に、java.util.Collection オブジェクトに対しては Java 8 で新たに定義された stream(), parallelStream() メソッドを呼び出すか、配列に対しては java.util.Arrays クラスに追加された static メソッド stream() を呼び出す、という方法があります。 後で見ますが、配列の場合は Stream#of() メソッドでも可能です(全ての要素からなる Stream オブジェクトを生成する場合)。 では各メソッドを見ていきましょう。
empty() メソッド
まずは空ストリームを生成する empty() メソッド:Stream<String> stream0 = Stream.empty(); assert stream0.count() == 0;
まぁ、型推論などもされるので簡単。 Collections クラスに Collection に対する似たような static メソッドありましたね。
of() メソッド
次は要素を列挙して Stream オブジェクトを生成する of() メソッド。 引数1つの場合は1つの要素からなるシングルトン・ストリーム(っていうのかな?)を生成します。 Collection に対する Collections#singleton(), singletonList() などに対応します。 可変長引数の of() メソッドはそのままそれらを要素とする Stream オブジェクトを返します:// of(T) Stream<String> stream1 = Stream.of("Java"); assert stream1.count() == 1; // of(T...) Stream<String> stream2 = Stream.of("Java", "Groovy", "Scala", "Clojure", "Kotlin", "Jython", "JRuby"); assert stream2.count() == 7; // 配列を渡してもいいよ String[] strArray = {"Java", "Groovy", "Scala", "Clojure", "Kotlin", "Jython", "JRuby"}; Stream<String> stream3 = Stream.of(strArray); assert stream3.count() == 7;
今までの記事のサンプルでもこれを多用してました。
generate() メソッド
次は要素数が無限個のストリームを生成する generate() メソッド。 引数に Supplier<T> オブジェクトをとります。 まずは0から100(0以上100未満)までの整数乱数を返すストリーム:// 乱数 Random random = new Random(1L); Stream<Integer> rand100 = Stream.generate(() -> random.nextInt(100)); rand100.limit(30).forEach(System.out::println);
無限に要素があるので、limit() メソッドによって30個に制限しています。 まぁ、なんてことないですね。 generate() の引数の Supplier オブジェクトは、もちろんラムダ式で書かないといけないわけではないので、以下のように無名クラスでオブジェクトを作ることもできます。 以下はよくありそうなフィボナッチ数列を生成するストリームを作るサンプル:
// フィボナッチ数列 Stream<Long> fib1 = Stream.generate(new Supplier<Long>(){ private long a = 0L, b = 1L; @Override public Long get(){ long newA = b, newB = a + b; a = newA; b = newB; return a; } }); fib1.limit(30).forEach(System.out::println);
LongStream でやった方がスッキリしそうだけど、まぁそんなに変わるってわけでもないのでご愛嬌。 出力結果は
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040
えぇー、あってるよね? ちなみにこれをラムダ式で
long x = 0L, y = 1L; Stream<Long> fib0 = Stream.generate(() -> { long newX = y, newY = x + y; x = newX; y = newY; return x; });
のように書こうとすると「ラムダ式から参照されるローカル変数は、finalまたは事実上のfinalである必要があります」という旨のメッセージとともにコンパイル・エラーを起こします。 まぁメッセージの通りですがね。 詳しくはid:nowokay 氏の「Java8のlambda構文がどのようにクロージャーではないか」参照のこと。 まぁ、Supplier に状態を持たせたい場合は(無名)クラスを書けということでしょうかね。
iterate() メソッド
iterate() メソッドも generate() メソッド同様、無限要素数のストリームを生成するメソッドです。 iterate() メソッドは2つの引数をとり、第1引数はストリームの初期値、第2引数は前の値から次の値を計算する漸化式をとります。 漸化式の部分は UnaryOperator<T> オブジェクトとして指定します。 まずは初期値1で、値を10倍していくストリーム(初項1、公比10の等比数列):// 10倍を繰り返して返す Stream<Integer> multi10 = Stream.iterate(1, i -> i * 10); multi10.limit(5).forEach(System.out::println);
実行結果は
1 10 100 1000 10000
簡単ですね。 次はもう少し複雑な(でもよくある)サンプル。 またしてもフィボナッチ数列ですが:
// フィボナッチ数列 その2 Stream<Long> fib2 = Stream.iterate( new Long[]{ 1L, 1L }, f -> new Long[]{ f[1], f[0] + f[1] }).map( f -> f[0] ); fib2.limit(30).forEach(System.out::println);
iterate() で生成しているストリームは要素数2の Long 配列で、そのストリームを生成した後、map() メソッドでその配列の第0要素を取り出しています。 ラムダ式では状態を保持しにくい(できない)ので(よくやられる)テクニカルな方法をやってみました。 出力結果はもちろん generate() の場合のフィボナッチ数列のサンプルと同じです。
builder() メソッド
最後は StreamBuilder オブジェクトを使ってストリームを生成する方法。 StreamBuilder は不変な String に対する可変な StringBuilder, StringBuffer に対応するインターフェースです*1。 使い方は- Stream#builder() メソッドによって StreamBuilder オブジェクトを生成する
- add() メソッド(もしくは accept() メソッド、効果は同じ)によって要素を追加
- build() メソッドによって Stream オブジェクトを生成
という手順になります。 build() メソッドによってストリームを生成した後は要素を追加することはできません。 サンプルはこんな感じ:
List<String> langs = Arrays.asList("Java", "Groovy", "Scala", "Clojure", "Kotlin", "Jython", "JRuby"); StreamBuilder<String> builder = Stream.builder(); for(String lang : langs) builder.add(lang); Stream<String> stream4 = builder.build(); stream4.forEach(System.out::println);
出力結果は
Java Groovy Scala Clojure Kotlin Jython JRuby
と、まぁそのままな感じ。 今の場合は Collection#stream() メソッドで同じことが簡単にできますが、各要素を生成するのに手間がかかる、別々に作るといった場合に使えます。
今回で Stream インターフェースに定義されているメソッドは完了。 次回はプリミティブ型のストリーム IntStream, LongStream, DoubleStream に特有のメソッドを見ていく予定。

- 作者: Miran Lipovača,田中英行,村主崇行
- 出版社/メーカー: オーム社
- 発売日: 2012/05/23
- メディア: 単行本(ソフトカバー)
- 購入: 25人 クリック: 580回
- この商品を含むブログ (71件) を見る
*1:並行性に関することが書かれていないので、おそらく StringBuilder に対応するものかと。