ITと創作でねこを飼いたい

プログラミングやITで勉強したこと、疑問をまとめます。

Java Gold SE8でStreamAPIめっちゃ出てくるのでまとめてみる。

StreamAPI何問出てくるんだ(そして何問間違えるんだ・・・)
一回整理しようと思います。
試験対策>>>実装なのでご注意ください。
思っていたより長くなったのでざっくりとしたことが知りたい人は赤字周辺だけ読んでください。

基本

StreamAPIとはJava SE 8で新たに導入された「データ集合の操作用API」。
データ集合の個々のデータに対して様々な操作を順次行うことができる。

中間操作と終端操作

StreamAPIを使用したデータ操作では、
ストリーム処理の中間で適応される中間操作
最後に一度だけ適応される終端操作の二種類が存在する。
各操作のポイントは以下の通り

中間操作のポイント
・ストリームオブジェクトを返す。
・メソッド呼び出しの時点で処理が実行される訳ではなく、終端操作の実行時に実行される→遅延評価
・終端操作が適応されるまで何度も(0回以上)適応することができる。

終端操作のポイント ・処理の最後に一度だけ適応することができる。
・終端操作が適用されたストリームに対して再度ストリーム処理を適用することはできない。
→再度ストリーム処理を行おうとすると「実行時に例外がスローされる」

具体的な各メソッド

Stream<T>インタフェースのメソッドだが、問題集やってると「全部出てないか?」ってなる。(主要なものについてここに書きます!)
公式ドキュメント→Stream (Java Platform SE 8)
上記リンクのJava SE APIをぼんやり眺めるだけでも、個人的には勉強になるのではないかと思っています。
特にメソッドの引数と戻り値が分かっておくと試験問題が解きやすい
引数にはラムダ式(SAM)がよく登場するので、そこも注意です!!!!

中間操作のメソッド

Stream<T> filter(Predicate<? super T> predicate)
predicateの記述に一致にするストリームを返す。

<R> Stream<R> map(Function<? super T,? extends R> mapper)
mapperの戻り値をストリームとして返す。

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
イメージとしてはコレクションを含んだstreamを各値の一連のストリームにするメソッド。

Stream<T> distinct()
重複を除いたストリームを返す。

Stream<T> sorted()
自然順序に並べてストリームを返す。ただしストリームの要素がComparableでない場合、例外がスローされる可能性がある。

Stream<T> sorted(Comparator<? super T> comparator)
こっちはComparator型のインスタンスを直接引数に取っている。つまりcomparatorに従って並べ替えたストリームを返す。

Stream<T> peek(Consumer<? super T> action)
よくコンソール出力とかをやっているところ。

Stream<T> limit(long maxSize)
maxSize個までのストリームを返す。

Stream<T> skip(long n)
nだけ要素を飛ばしたストリームを返す。

終端操作のメソッド

void forEach(Consumer<? super T> action)
ストリームの各要素に対して処理を行う。

void forEachOrdered(Consumer<? super T> action)
定義された実行順に従い、ストリームの各要素に対して処理を行う。

long count()
ストリームの要素の個数を返す。

boolean anyMatch(Predicate<? super T> predicate)
ストリームの各要素のうち、一つでも条件を満たせばtrue

ちなみに

       boolean result = Stream.of(1,2,3,4).anyMatch(p -> p == 2);
        System.out.println(result);

を実行してみましたが「true」が一つだけ出力されました。
(ブログ書きながら、一瞬 false true false..とかfalse trueとか出てくるのかと思った)

boolean allMatch(Predicate<? super T> predicate)
全てが条件を満たせばtrue

boolean noneMatch(Predicate<? super T> predicate)
全てが条件を満たさないのであればtrue

Optional<T> findFirst()
ストリームの最初の要素を返す。Optionalについては後述

Optional<T> findAny()
順序は保証されていないが、最初に見つけたストリームの要素を返す。

Optional<T> min(Comparator<? super T> comparator)
comparatorに従って、ストリーム要素中の最小値を返す。

Optional<T> max(Comparator<? super T> comparator)
comparatorに従って、ストリーム要素中の最大値を返す。

試験対策の呪文1
Stream<T>インタフェースではmin、maxメソッドに必ず引数が必要

R collect(Supplier supplier,
BiConsumer<R,? super T> accumulator,
BiConsumer<R,R> combiner
)
ストリームに対して可変リダクション操作を行う(今回は深掘りしない)
BiConsumerは二つの引数をとって結果を返さないラムダ式の型

そして終端操作で必ずといっていいほど出るのがreduce
おそらくメソッド名は「減らす」というより「縮める」といった意味合いで名付けられているのだと思う。
reduceメソッドには基本的に、以下の二つの種類が存在する。
(実はもう一種類存在するのだが、reduceメソッドの具体的な使い方も含めて、以下のサイトを参考にして欲しい)

qiita.com

T reduce(T identity,
BinaryOperator<T> accumulator
)
(初期値)identityから引数を順番に二つとって引数と同じ型を返すaccumulator(BinaryOperator)の処理を行う

Optional<T> reduce(BinaryOperator<T> accumulator)
ストリームから引数を二つとって、引数と同じ型の結果を返す。

試験的に大事なのは戻り値の型が違うこと

Optional<T>はAPIドキュメントをみる限り、「もしかしたらnullかもしれないけど、nullも含めて値として入れておくね」っていう型っぽい。中身の値を取り出すにはJava Gold SE8の試験では2/3くらいはget()メソッドが用いられている。(慎重に処理するならOptional<T>の別のメソッドの方が良さそう)

要約すると、、、
試験対策の呪文2
引数を一つしか取らないreduceメソッドでは結果の値を取り出すためにgetメソッドが必要

もちろん他のOptional<T>を返すメソッド(minとかmaxとか)にもgetは必要!!!!

Streamインタフェースの種類

f:id:ashanoguzyutu:20180717004002j:plain

基本はStream<T>インタフェースなのだが、他のもちょくちょく出てくるので厄介。
上の図を見てもらったら分かるが、StreamAPIはどれもBeseStreamを継承している。

StreamとIntStreamの違いは?

Stream<T>インタフェースは参照型(T型)のデータ集合を扱うためのインタフェース
IntStreamインタフェースはInt型(プリミティブ型)データ集合を扱うためのインタフェース。

ここだけ読むと「ああ、そう」ってかんじ。

ただJava APIドキュメントを見比べると分かるが、
Stream<T>インタフェースとIntStreamインタフェースでは、メソッドの内容が似ている割にintStreamインタフェースの方がメソッドの数が多い

数え間違えがなければ、Stream<T>インタフェースは39個に対しintStreamインタフェースは44個のメソッドが存在する。

増えたメソッドの例

int sum()
IntStreamの要素の合計を返す。終端操作。

OptionalDouble average()
IntStreamの要素の平均を返す。終端操作。

つまり、中身がint型だと分かっている分、int型に特化したメソッドが追加されたということ
数値だと分かっていなければsumなんてメソッドは用意できない訳だ。
Int型(プリミティブ型)データ集合を扱うためのインタフェースとはそういった意味を指す。

Stream<T>インタフェースにも存在するが、かなり変化のあったメソッド

OptionalInt min()
このストリームの最小要素を記述するOptionalint(または空のOptional)を返す。

OptionalInt max()
このストリームの最大要素を記述するOptionalint(または空のOptional)を返す。

ここで呪文1の効力が出てくるのだが
試験対策の呪文3
InStreamインタフェースのmin,maxには引数はない

さらにOptionalIntにもgetは必要か?という話になる訳だが、ここではgetではなくgetAsIntメソッドが必要。。 ややこしい。。

あとIntStreamインタフェースで押さえておきたいのは、、
試験対策の呪文4
InStreamインタフェースのメソッドにラムダ式(SAM)の型が入る場合、ほぼIntから始める

何を言っているんだ、、となっているかもしれないが、
つまりStream<T>インタフェースで引数の型がFunction型となっていたところはIntFunction型になっている、という意味。

Stream<T>インタフェースの場合
filter(Predicate<? super T> predicate)

IntStreamインタフェースの場合
filter(IntPredicate predicate)

てな具合。
collectとかだと少し話は違ってくるようだが、概ねこのルールに従っている。
Functionインタフェースをよく理解している人なら、処理しているのがint型なんだか当たり前やんってなるのかもしれない。

Stream<T>からIntStreamへの変換

中間操作のメソッド(飛ばしてた)のmapToIntを用いる方法がJava SE8 Goldでは一般的
定義↓
IntStream mapToInt(ToIntFunction<? super T> mapper)

同じように他のStreamインターフェースに対してmapToLong,mapToDoubleといったメソッドが存在する。逆にIntStreamからStream<T>に変換する場合はmapToObjectboxed()と言ったメソッドが用いられる。

どのStreamインタフェースも自分に変換する以外のメソッド(IntStreamならmapToInt)は定義されているようだ。

総集編

「「Inteager型の数値の入ったストリームオブジェクトで、引数を取らないmaxメソッドを用いて最大値を出力する」」

プログラムは以下のようになり、コンソールには4が出力される。

       int num = Stream.of(1,2,3,4)           //Stream<T>のストリームオブジェクト
                  .mapToInt(l -> l)        //IntStreamのストリームオブジェクトに変換
                  .max().getAsInt();      //IntStreamインタフェースのmaxメソッドの利用
        System.out.println(num);

まだまだ細かいことを言い出したらきりがないですが以上です。
StreamAPIの問題を解くときの迷いが少しでも減ったらいいなぁ、と思っております。