Java 8から鳴物入りで導入されたStream APIと、古き良きforループからの移行論争について適当な感想。「※個人の感想です。」
適当にググって出てくる紹介ページの基本論調としては、「forループなんて時代遅れ!これからは全部Stream APIで処理すべし!forループは全部書き換えろ!」が多数派な印象。まあ、新技術の紹介が目的だからそうだよねという感じ。
一方で、無理やりStream APIで書くためにpeek()
中間操作を副作用のあるロジック記述に使ってたり、forEach
/forEachOrdered
終端操作の違いも気にせず使ってたりするのを見かけると、そこまでしてStream APIを積極推進すべきかぁ?とも思う。
結構な割合で、ちょっとparallelストリームに変えるだけで「正しく動く保証のない」Stream処理が書かれている気がする。sequentialにしか正常動作しないなら、気張ってStream APIで書き換えなくてもいいのではという気分になる。インデクス値必須なら古典的forループも悪くないし、単純な要素走査なら拡張forループで十分なこともあるでしょ?(ループ内処理は適切に構造化されている前提)
Stream APIの真価は逐次/並列処理のシームレス統合で、それはJSR-335でも掲げられていたはず。一方で、高速化を目的とする並列処理で捉えると、そのループは最適化対象に値する処理量・データ量なのかに疑問符。最内ループだけいくら頑張っても駄目。早すぎる最適化は諸悪の根源。
その意味で、よくあるStream APIのサンプルは局所化されすぎてるし(サンプルだから仕方ないけど…)、上位の処理構造でデータ並列を効果的に使えるのかという疑問はある。大局的にはFork/Joinタスク並列の方がずっと効果あるんじゃない?残念ながら使い勝手は今一つで、だからこそStream APIが導入されたんだけど。
あとStream API並列処理でちゃんと処理性能出すには、裏方の仕組みを理解していないとやっぱり無理だと思う。Spliterator特性とかCollector特性とか、まだ解説記事も少ない印象。あと並列化オーバーヘッド無視されすぎ。並列処理は魔法じゃないんですよ。
結局のところは、手続型で副作用ベースの記述がされていたforループを、関数型へのパラダイムシフトなしに見た目だけStream API使っても上手くないという話か。表層的な新機能紹介よりも、考え方がなにより重要ではという感想。関数型云々言い出すと怖いからこのへんで。
参考資料: