実装1、2のような繰り返し処理があるとします。この繰り返し処理はある条件に合致すると処理を終了します。
この場合に効率的な実装は1、2のどちらだと思いますか。個人的にはStreamの使う実装2の方が非効率だと感じます。(必ず10000の変数が生成されてしまうため)
Stream APIが登場してから「forは使うな!」というような記事を多くみかけ、はたして本当に使うべきではないのか疑問に思った次第です。
※並列化は考えません。
Java
1//実装1 2public void hoge(){ 3 for(long x = 1; x < 10000; x++){ 4 if(条件xxxx){ 5 return; 6 } 7 8 return; 9} 10 11// 実装2 12public void fuga(){ 13 LongStream.range(1, 10000).forEach(x -> { 14 if(条件xxxx){ 15 return; 16 } 17 }); 18 19 return; 20}
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答3件
0
ベストアンサー
Java 9からはtakeWhile()
が使えます。
Java
1import java.util.stream.LongStream; 2 3public class StreamWhile { 4 public static void main(String[] args) throws Exception { 5 hogeFor(); 6 hogeStream(); 7 hogeStreamTakeWhile(); 8 } 9 10 public static void hogeFor() { 11 for (long x = 1; x < 10000; x++) { 12 if (x > 10) { 13 return; 14 } 15 System.out.println(x); 16 } 17 } 18 19 public static void hogeStream() { 20 LongStream.range(1, 10000).forEach(x -> { 21 if (x > 10) { 22 return; 23 } 24 System.out.println(x); 25 }); 26 } 27 28 // Java 9 から使える takeWhile を使用 29 public static void hogeStreamTakeWhile() { 30 LongStream.range(1, 10000).takeWhile(x -> x <= 10).forEach(x -> { 31 System.out.println(x); 32 }); 33 } 34}
三つのメソッドの出力は同じです。条件チェックが動く回数はhogeFor()
が11回に対して、hogeStream()
が10000回も動作すると言うことが問題だと言うことだと思います。しかし、takeWhile()
を使ったhogeStreamTakeWhile()
は同じく11回に抑えられます。
ただし、このメソッドはJava 9からです。2月25日現在、Java 9はまだベータ版です。Java 9の正式リリースは2017年7月27日の予定です。あと5ヶ月ほどですが、気長に待ってください。
なお、とても個人的な見解ですが、Java 8から追加されたStreamは使えば使うほど、あれが無い、これが無いと不満が募るものです。Optionalやラムダ式も追加されましたが、同様です。今回、Java 9でtakeWihle()
が追加されることになりましたが、未だにzip()
がありませんし、そもそもタプルがありません。Map.Entry<K,V>
をペア代わりに使うとかのテクニックが堂々と述べられるとか、どこかおかしいです。Javaで関数型プログラミングを本格的にするには、痒いところに手が微妙に届かない、というとても残念な作りになっています。「forを使うな、Streamを使え!」というのであれば、「Javaを使うな、Scalaを使え!」と言った方がマシと私個人は思っています。
投稿2017/02/25 11:27
総合スコア21741
0
個人的にはStreamの使う実装2の方が非効率だと感じます。(必ず10000の変数が生成されてしまうため)
java のコンパイラもライブラリもそんなにバカではありません(と、私は信じてます)。
for 文だとその中の処理は順次にしか実行できませんが、ストリーミングであれば、パイプライン処理ができる可能性があります。
for (long x = 1; x < 10000; x++) {
処理1
処理2
処理3
}
となっている場合、 x=1 の処理3が終わるまで x=2 の処理1は始まりませんが、
LongStream.range(1, 10000)
.処理1
.処理2
.処理3
となっていれば、 x=1 の処理1と同時に(場合によって) x=2 の処理1が始められます。
たとえば、処理3がデータベースへの問い合わせだったりする場合、前者では、x=1 に対するデータベースサーバから応答が返ってくるまでCPUは待つしかありませんが、後者であれば他のことができるかもしれません。
したがって、どちらかというと、ストリーミングのほうが最適化の余地があるものと思われます。
ただし、parallel() を呼んで、並列モードにしないと、並列実行されません。
以下にパイプラインを並列実行する例を示します。
java
1import java.util.stream.LongStream; 2import java.util.Random; 3class test { 4 private static void safeSleep(int t) { 5 try{Thread.sleep(t);} catch(Exception e){} 6 } 7 public static void main(String argv[]) { 8 Random rnd = new Random(); 9 LongStream.range(1, 10000).parallel() 10 .map(x -> { 11 int t = rnd.nextInt(10); 12 System.out.println("process1 x=" + x + " sleep " + t + "ms"); 13 safeSleep(t); 14 return x; 15 }) 16 .map(x -> { 17 int t = rnd.nextInt(10); 18 System.out.println("process2 x=" + x + " sleep " + t + "ms"); 19 safeSleep(t); 20 return x; 21 }) 22 .forEach(x -> { 23 int t = rnd.nextInt(10); 24 System.out.println("process3 x=" + x + " sleep " + t + "ms"); 25 safeSleep(t); 26 return; 27 }); 28 } 29}
投稿2017/02/25 12:40
編集2017/02/25 23:08総合スコア3401
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
自分は「forは使うな!」が意味していることはおそらく「streamで充分すっきり書けるようになったことを知らずになんでもかんでもforループを使ってないですか?もしそうならstreamも知っておいた方がいいですよ」という程度のことだと思っています。
自分はこのループがボトルネックになるほどクリティカルな場面では前者を選択します。例えばこのループがnanosecoundレベルの効率を求めるようなケースです。そうとは限らないところなら
java
1LongStream.range(1, 10000) 2 .filter(predicate) 3 .findFirst() 4 .ifPresent(lv -> ...);
なんてコードを書きたくなることもあります。単にreturnとかbreakとかcontinueをかかなくてもすむならそうしようかなという程度なのでその辺りは好みかも知れません。
Streamがfor文にとってかわれるほどの記述能力を持っているかというとそういう訳でもありませんが世の中に大量にあるfor文の中の多くにStreamで書いた方がわかりやすいとプログラマーが感じるものが多いというのも事実だと思います。要するに適宜使い分ければよいと思います。
ちょっと横道かも知れませんがJavaのstreamは少々機能に見劣りする点がありtake/dropがない(java9でようやく入るみたいですが)とかそもそもStream<long>じゃなくてLongStreamなのかい!などの不満があるので「今のところは自然に使えるところに使う」のでいいのかなぁなんて思います。
投稿2017/02/25 10:51
総合スコア18404
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/02/25 13:37
2017/02/25 13:52
2017/02/25 23:06
2017/02/26 04:35