Java Stream APIうたくいくものずそうでないもの





Java 8 Stream APIはずおも「゚ネルギッシュ」ですか コレクションの耇雑な操䜜の凊理を単玔で理解可胜なコヌドに「倉える」こずは可胜ですか 䞊列操䜜の利点はどこにあり、い぀停止するのですか これらは、読者が遭遇する倚くの質問の䞀郚です。 @irnyずも呌ばれるTagir Valeevを䜿甚しお、Stream APIの萜ずし穎を理解しおみたしょう。 倚くの読者は、䌚議での蚘事、Javaの研究、および衚珟力豊かなレポヌトに぀いおの察話者にすでに粟通しおいたす。 ですから、遅滞なく、議論を始めたす。



-Tagir、StackOverflowリ゜ヌスのパフォヌマンスが優れおいたす java-streamブランチのゎヌルドステヌタス 。 Java 8 Stream APIを䜿甚するダむナミクスず蚭蚈の耇雑さがこのリ゜ヌスに関する質問ず回答に基づいお倧きくなったず思いたすか



-確かに、か぀おStackOverflowに倚くの時間を費やし、Stream APIの問題を垞に監芖しおいたした。 私の意芋では、最も興味深い質問ぞの答えがすでにあるので、私は定期的に調べたす。 もちろん、人々はStream APIを詊しおみたように感じたすが、詊しおみないずおかしいでしょう。 このトピックに関する最初の質問は、人々が初期ビルドを実隓したJava 8がリリヌスされる前から珟れおいたした。 党盛期は2014幎ず2015幎の終わりに来たした。

倚くの興味深い質問は、Stream APIでできるこずだけでなく、サヌドパヌティのラむブラリなしでは通垞できないこずにも関連しおいたす。 ナヌザヌは、垞に質問ず議論を重ねながら、Stream APIの範囲を拡倧しようずしたした。 これらの質問のいく぀かは、Java 8 Stream APIの機胜を拡匵するStreamExラむブラリのアむデアの源になっおいたす。



-StreamExに蚀及したした。 創䜜のきっかけを教えおください。 あなたの目暙は䜕ですか



-動機は玔粋に実甚的でした。 仕事でJava 8に切り替えたずき、矎しさず䟿利さからの最初の陶酔感はすぐに䞀連の旅行に眮き換えられたした。私は、実行すべきStream APIで特定のこずをしたかったのですが、うたくいきたせんでした。 コヌドを長くするか、仕様から逞脱する必芁がありたした。 これらの問題を解決するために、䜜業䞭のプロゞェクトに補助クラスず補助メ゜ッドを远加し始めたしたが、芋苊しくなりたした。 その埌、クラスで暙準ストリヌムをラップするず、倚くの远加操䜜が提䟛され、䜜業がずっず楜しくなりたした。 これらのクラスを別のオヌプンプロゞェクトで遞択し、開発を開始したした。



- あなたの意芋では、どのような皮類の蚈算ず操䜜、およびどのデヌタにStream APIを䜿甚しお実装する䟡倀がありたすか。たた、凊理にあたり適さないものは䜕ですか。



-ストリヌムAPIは䞍倉デヌタが倧奜きです。 新しいデヌタ構造を䜜成するのではなく、既存のデヌタ構造を倉曎する堎合は、別のものが必芁です。 新しい暙準メ゜ッドList.replaceAllなどに泚目しおください。



ストリヌムAPIは独立したデヌタが倧奜きです。 入力セットの耇数の芁玠を同時に䜿甚する必芁がある堎合、サヌドパヌティのラむブラリがないず非垞に䞍栌奜になりたす。 しかし、StreamExのようなラむブラリは、倚くの堎合、この問題を解決したす。



ストリヌムAPIは、パスごずに1぀の問題を解決するのが奜きです。 1回のデヌタラりンドでいく぀かの異なる問題を解決する堎合は、独自のコレクタヌを䜜成する準備をしおください。 そしお、それがたったくうたくいくずいう事実ではありたせん。



ストリヌムAPIは、チェックされた䟋倖を奜みたせん。 Stream APIオペレヌションからそれらをスロヌするこずはあたり䟿利ではありたせん。 繰り返しになりたすが、これを簡単にしようずするラむブラリヌたずえばjOOλ がありたすが、チェック枈みの䟋倖を攟棄するこずをお勧めしたす。



暙準のStream APIには、非垞に必芁な操䜜がいく぀かありたせん。 たずえば、takeWhileはJava 9でのみ衚瀺されたす。非垞に合理的で耇雑でないものが必芁な堎合がありたすが、これを行うこずはできたせん。 繰り返したすが、jOOλやStreamExのようなラむブラリがこれらの問題のほずんどを解決するこずは泚目に倀したす。







- 垞にparallelStreamを䜿甚するのが理にかなっおいるず思いたすか メ゜ッドをストリヌムからparallelStreamに「切り替える」ずきに発生する可胜性のある問題は䜕ですか



-どのような堎合でも、垞にparallelStreamを䜿甚しないでください。 非垞にたれにしか䜿甚されず、これには十分な理由があるはずです。



たず、Stream APIを䜿甚しお解決されるタスクのほずんどは、ForkJoinPoolのタスクを分散しお同期するオヌバヌヘッドに比べお速すぎたす。 Doug Leaの有名な蚘事「 い぀パラレルストリヌムを䜿甚するか 」は経隓則です。珟代のマシンでは、通垞、実行時間が100マむクロ秒を超えるタスクをパラレル化するのが理にかなっおいたす。 私のテストでは、䞊列化によっお20マむクロ秒のタスクが加速されるこずがありたすが、これはすでに倚くの芁因に䟝存しおいたす。



第二に、タスクが長時間実行されたずしおも、䞊列凊理がそれを加速するずいう事実ではありたせん。 これは、゜ヌスの品質、䞭間操䜜たずえば、順序付けされたストリヌムの制限が長時間にわたっお機胜する、および端末操䜜たずえば、forEachOrderedが䞊行性の利点を無効にする堎合があるに䟝存したす。 最適な䞭間操䜜はステヌトレス操䜜フィルタヌ、マップ、flatMap、およびピヌクであり、最適な最終操䜜はリデュヌス/コレクトファミリヌです。これは連想的です。぀たり、タスクを効率的にサブタスクに分割し、結果を結合できたす。 たた、マヌゞ手順が最適でない堎合もありたすたずえば、耇雑なgroupingByチェヌンの堎合。



第䞉に、倚くの人々がStream APIを誀っお䜿甚し、仕様に違反しおいたす。 たずえば、ステヌトフル状態のラムダをフィルタヌやマップなどの操䜜に枡したす。 たたは、reduceのナニット芁件ず結合性に違反しおいたす。 間違ったコレクタヌがいく぀曞いおいるかは蚀うたでもありたせん。 これは倚くの堎合、連続するストリヌムでは蚱されたすが、䞊列ストリヌムでは完党に受け入れられたせん。 もちろん、これは間違っお蚘述する理由ではありたせんが、事実は明らかです。パラレルストリヌムを䜿甚するこずはより難しく、単にどこかにparallelを远加するだけではありたせん。



そしお最埌に、ストリヌムが長時間実行されおいおも、その䞭の操䜜は簡単に䞊列化でき、すべおを正しく実行しおいたす。プロセッサコアが実際にアむドル状態であるかどうかを怜蚎する䟡倀がありたす。 リク゚ストが垞にロヌドされるWebサヌビスがある堎合、各リク゚ストを個別のスレッドで凊理する方が合理的である可胜性が非垞に高くなりたす。 倚数のコアがある堎合、たたはシステムが完党にロヌドされおいない堎合にのみ、パラレルストリヌムに぀いお考えるこずができたす。 おそらく、䞊列ストリヌムを制限するためにjava.util.concurrent.ForkJoinPool.common.parallelismをむンストヌルする䟡倀があるかもしれたせん。



たずえば、コアが16個あり、通垞12個がロヌドされおいる堎合、残りのコアをストリヌムで占有するように䞊列床レベル4を蚭定しおみおください。 もちろん、䞀般的なヒントはありたせん。垞に確認する必芁がありたす。



- 䞊列化の議論を続けるず、パフォヌマンスはデヌタの量ず構造、プロセッサコアの数に圱響されるず蚀うこずはできたすか どのデヌタ゜ヌスLinkedListなどを䞊列凊理するべきではありたせんか



-LinkedListはただ最悪の゜ヌスではありたせん。 少なくずも圌は自分のサむズを知っおいるので、Stream APIはタスクをよりうたく分割できたす。 最悪なのは、䞊列凊理のために、本質的に䞀貫性のある゜ヌスLinkedListなどで、サむズを報告しない゜ヌスです。 通垞、これはSpliterators.spliteratorUnknownSizeを通じお、たたはサむズを指定せずにAbstractSpliteratorを通じお䜜成されたす。 JDKの䟋は、Stream.iterate、Files.list、Files.walk、BufferedReader.lines、Pattern.splitAsStreamなどです。 この点に぀いおは、今幎のJPointに関するStreamPoint Odditiesレポヌトで説明したした。 実装が非垞に悪いため、たずえば、この゜ヌスに含たれる芁玠が1024個以䞋の堎合、たったく䞊列しないずいう事実に぀ながりたす。 そしお、それでもかなりひどく䌌おいたす。 倚かれ少なかれ通垞の䞊列凊理を行うには、䜕䞇もの芁玠が必芁です。 StreamExの実装は改善されおいたす。 たずえば、StreamEx.ofLinesリヌダヌBufferedReader.linesの類䌌物は、小さなファむルでも十分に䞊列したす。 悪い゜ヌスがあり、それを䞊列化する堎合、最初に順番にリストに収集する方が効率的ですたずえば、Stream.iterate....collecttoList。ParallelStream...



ほずんどの暙準JDKデヌタ構造は優れた゜ヌスです。 Java 7ず互換性のあるサヌドパヌティラむブラリの構造ずラッパヌに泚意しおください。Spliteratorメ゜ッドをオヌバヌラむドできないためJava 7にはスプリッタがないため、Collection.spliteratorたたはList.spliteratorの実装を䜿甚したす。デフォルトでは、もちろん、デヌタ構造に぀いお䜕も知らず、単玔にむテレヌタをラップするため、うたく䞊列したせん。 9では、これはランダムアクセスリストで改善されたす。



- 䞭間操䜜を䜿甚する堎合、あなたの意芋では、Stream パむプラむンの しきい倀はどのようなもので、 どのように決定されたすか 制限明瀺的および暗黙的はありたすか



-厳しい制限があるずは蚀いたせん。 ほずんどの䞭間Stream API操䜜は、1回の呌び出しでスタックの深さを増やしたす。 䞀方で、これはむンラむン化の効率を䜎䞋させる可胜性がありたすHotSport JVMのデフォルトではMaxInlineLevel = 9。 しかし、通垞、これは孀立したベンチマヌクでのみ圹割を果たしたす。 実際のアプリケヌションでは、呌び出しは䟝然ずしお倚態的であり、むンラむン化を最倧限に掻甚できたせん。 長いスタックトレヌスは、ログを詰たらせたり、䟋倖オブゞェクトの䜜成を遅くしたり、初心者を怖がらせるだけです。 しかし、䞀般的に、それらは無害です。 本圓に必芁な堎合は、数十の䞭間操䜜を実行しおも問題ありたせん。 もちろん、䟋えばwhile䜕かstream = stream.mapx-> ...ルヌプのように、制埡䞍胜にストリヌムを増やすべきではありたせん。 タヌミナル操䜜の埌続の実行には、StackOverflowErrorから萜ちるリスクがありたす。



凊理䞭にコレクションを敎理する方法䞭間の䞊べ替え操䜜たたは順序付けされたデヌタ゜ヌスを䜿甚し、マップ、フィルタヌ、および操䜜の削枛を䜿甚しおそれを操䜜する方法は、パフォヌマンスの向䞊に぀ながりたすか



いいえ、ほずんど。 入力が゜ヌトされるずいう事実を利甚するのは、distinct操䜜のみです。 アルゎリズムを倉曎し、芁玠を前の芁玠ず比范したす。゜ヌトせずにHashSetを保持する必芁がありたす。 ただし、このために、゜ヌスは゜ヌトされおいるこずを報告する必芁がありたす。 JDKからのすべおの゜ヌトされた゜ヌスBitSet、TreeSet、IntStream.rangeにはすでに䞀意の芁玠が含たれおいるため、distinctは圹に立たない。 理論的には、セットの前半がtrueで埌半がfalseの堎合、プロセッサでの分岐予枬が優れおいるため、フィルタヌ操䜜は䜕かに勝぀こずができたす。 ただし、デヌタが既に述語で゜ヌトされおいる堎合は、Stream APIを䜿甚せず、バむナリ怜玢を䜿甚しお境界線を芋぀ける方が効率的です。 さらに、入力デヌタの゜ヌトが䞍十分な堎合、゜ヌト自䜓が遅くなりたす。 したがっお、゜ヌトの堎合、ランダムデヌタのDistinctは、distinct自䜓が高速化されたすが、distinctよりも䜎速になりたす。



- コヌドのデバッグに関連する重芁な問題を提起する必芁がありたす。 peekメ゜ッドを䜿甚しお䞭間結果を取埗したすか 独自のテストの秘密を持っおいる可胜性はありたすか それらを読者ず共有しおください。



-䜕らかの理由で、デバッグにpeekを䜿甚したせん。 ストリヌムが非垞に耇雑で、プロセスで䞍可解なこずが発生した堎合、それを耇数の䞭間リストを䜿甚しお分割しお、このリストを確認できたす。 䞀般に、IDEの通垞のステップバむステップデバッガヌでストリヌムをバむパスするこずに慣れるこずができたす。 最初は怖いですが、それに慣れたす。



新しいスプリッタヌずコレクタヌを開発するずき、包括的なテスト、さたざたな䞍倉条件のチェック、異なる条件䞋での起動を行うテストで補助メ゜ッドを䜿甚したす。 パラレルストリヌムずシリアルストリヌムの結果が同じであるこずを比范するだけでなく、パラレルストリヌムに人工スプリッタヌを挿入できたす。これにより、パラレルタスクを䜜成するずきに空のフラグメントが生成されたす。 結果に圱響を䞎えおはならず、重芁なバグを芋぀けるのに圹立぀はずです。 たたは、スプリッタヌをテストするずき、それらをランダムにただし同じスレッドで実行するサブタスクにランダムに分割し、結果を順次のものず比范したす。 これは安定した再珟可胜なテストであり、シングルスレッドですが、䞊列スプリッタヌでほずんどの゚ラヌをキャッチできたす。 䞀般に、すべおのコヌドブロックを包括的にチェックし、゚ラヌが発生した堎合に健党なレポヌトを発行するクヌルなテストシステムは、通垞デバッグを完党に眮き換えたす。



- 今埌、Stream APIのどのような開発が芋られたすか



-難しい質問です。将来を予枬する方法がわかりたせん。 珟圚、倚くは4぀のStream APIの特殊化Stream、IntStream、LongStream、DoubleStreamの存圚に䟝存しおいるため、倚くのコヌドを4回耇補する必芁がありたす。 誰もがゞェネリックスペシャラむれヌションを楜しみにしおいたす。ゞェネリックスペシャラむれヌションはJava 10で完成する可胜性がありたす。そうすれば簡単になりたす。



Stream API拡匵機胜にも問題がありたす。 ご存じのように、Streamはむンタヌフェむスであり、䜕らかの最終クラスではありたせん。 䞀方で、これにより、サヌドパヌティの開発者がStream APIを拡匵できたす。 䞀方、Stream APIに新しいメ゜ッドを远加するのはそれほど簡単ではありたせん。Java8でこのむンタヌフェヌスを既に実装しおいるすべおのクラスを壊す必芁はありたせん。 新しいメ゜ッドはそれぞれ、既存のメ゜ッドの芳点から衚されたデフォルトの実装を提䟛する必芁がありたすが、これは垞に可胜で簡単ではありたせん。 したがっお、機胜の爆発的な成長はほずんど期埅できたせん。



Java 9で衚瀺される最も重芁なこずは、takeWhileおよびdropWhileメ゜ッドです。 Stream.ofNullable、Optional.streamの3぀の匕数ずいく぀かの新しいコレクタヌ-flatMapping、filteringで反埩する小さな楜しいものがありたす。 しかし、党䜓ずしお、ただ倚くが芋萜ずされたす。 しかし、JDKにはストリヌムを䜜成する远加のメ゜ッドがありたす。新しいAPIは珟圚ストリヌムに泚目しお開発されおおり、叀いAPIは匷化されおいたす。



- 倚くの人が、2015幎のスピヌチを「私たちは䜕を枬定しおいるのですか」ずいうレポヌトで思い出したした。 今幎は新しいゞョヌカヌのテヌマを蚈画しおいたすか それはどうなるのでしょうか



-新しいレポヌトを䜜成するこずにしたしたが、「Stream API Fads」ずはあたり創造的ではありたせん。 これは、ある意味で、JPointのレポヌト「ストリヌムAPIの奇異」の続きです。Java9で修正されるものに焊点を圓おお、ストリヌムAPIの予期しないパフォヌマンス効果ず滑りやすいスポットに぀いお説明したす。



- 興味深く、詳现な回答をありがずうございたす。 新しいパフォヌマンスを楜しみにしおいたす。






Joker 2016カンファレンスでは、Stream APIおよびその他のJavaハヌドコアの䞖界に觊れるこずができたす。 講挔者ぞの質問、レポヌトに関する議論、無限のネットワヌキングもありたす。



All Articles