質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

23201閲覧

Streamを途中で分岐させたい

kakusuke

総合スコア80

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

0クリップ

投稿2015/07/30 17:20

編集2015/07/30 18:53

stream を途中で分岐させたいです
例えば、得点が60点以上だった場合に1ポイント、80点以上だった場合に2ポイント、90点以上は 3 + (n - 90) ポイント付与する条件で、ポイントの合計が欲しい場合。
streamを分岐できれば簡単に書けると思うのですが、やりようはないでしょうか

コードのイメージとしてはこんなかんじです

Java

1Stream<Integer> stream = Stream.of(1, 2, 3, 60, 65, 85, 90, 92, 95); 2Integer sum = stream.flatMap( 3 fork( 4 branch(n -> n < 60).map(n -> 0), 5 branch(n -> n < 80).map(n -> 1), 6 branch(n -> n < 90).map(n -> 2), 7 branch().map(n -> 3 + (90 - n)) 8 ) 9 ) 10 .mapToInt(Integer::valueOf) 11 .sum();

ちなみに、途中で終端処理を挟まない方法を模索中です

ikuwow👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

実現したいのは、下記のようなデーターフローでしょうか?

[Source] --> [FilterX] --> [Split] --> [FilterA] --> [SinkA] +------> [FilterB] --> [SinkB]

残念ながら、Java8のストリーム(java.util.stream.Stream)は、汎用のデータフロー・パイプライン処理のための仕組みではありません。出力先が2個以上あるような「分岐」は表現不可能です。

1つのストリームは、必ず「1つのソース」+「0個以上の中間操作」+「1つの終端操作」から構成されます。

Streamが何を目的としたものであるかや、その内部的な動作については 社内Java8勉強会 ラムダ式とストリームAPI が参考になるかと思います。

投稿2015/07/31 14:28

yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kakusuke

2015/07/31 15:29

なるほど、原理的に無理なんですね・・・ 理解しました、ありがとうございます。
guest

0

グルーピング化して、それぞれ計算するようにしてみるぐらいしか方法はないかなと思います。
並列化もしているので、時間がかかる処理もマルチスレッドでよしなにやってくれるはずです。
こんな感じでいかがでしょうか?
(もうちょっとうまいか書き方はあるかもしれません)

Java

1import java.util.stream.*; 2 3public class SumPoint2 { 4 public static void main(String[] args) { 5 Stream<Integer> stream = Stream.of(1, 2, 3, 60, 65, 85, 90, 92, 95); 6 Integer sum = stream.collect(Collectors.groupingByConcurrent((n) -> { 7 if (n >= 90) { 8 return "A"; 9 } else if (n >= 80) { 10 return "B"; 11 } else if (n >= 60) { 12 return "C"; 13 } else { 14 return "Z"; 15 } 16 })).entrySet().parallelStream().mapToInt((e) -> { 17 switch(e.getKey()) { 18 case "A": 19 return e.getValue().stream().mapToInt(n -> 3 + (n - 90)).sum(); 20 case "B": 21 return 2 * e.getValue().size(); 22 case "C": 23 return e.getValue().size(); 24 } 25 return 0; 26 }).sum(); 27 28 System.out.println(sum); // => 20 29 } 30}

以下は普通にする、修正前の回答です。

Streamを使うのであれば、処理を分岐させるのではなく、
各ポイントにマッピングすると考えた方がいいです。
下のような感じはいかがでしょうか?

Java

1import java.util.stream.*; 2 3public class SumPoint { 4 public static void main(String[] args) { 5 IntStream stream = IntStream.of(1, 2, 3, 60, 65, 85, 90, 92, 95); 6 Integer sum = stream 7 .map((n) -> { 8 if (n >= 90) { 9 return 3 + (n - 90); 10 } else if (n >= 80) { 11 return 2; 12 } else if (n >= 60) { 13 return 1; 14 } else { 15 return 0; 16 } 17 }) 18 .sum(); 19 System.out.println(sum); // => 20 20 } 21}

投稿2015/07/30 22:06

編集2015/07/31 10:22
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kakusuke

2015/07/30 23:59

ご回答ありがとうございます ただすみません、あくまで例であって、やりたいことはstreamの分岐なんです 集合の中の部分集合毎単位で処理を記述したいんです 回答していただいた方法だと、要素単位での記述になるので、すみませんが求めている答えではないです
raccy

2015/07/31 10:23

いまいちしたいことのイメージがつかみ切れてませんが、回答を修正してみました。 集合毎単位で処理・・・していると思います。
kakusuke

2015/07/31 15:40

説明が足りずすみません、、、 stream で書いてる最中に、まさに2つめのように一旦終端処理を書いたあとentrySetで再度ストリームを作るみたいな書き方になってしまったコードがありました このままだと可読性とメモリ効率が悪いので、ストリームの中間処理としてサブストリームを作り、それぞれのサブストリームの中で集計し、本流に合流できればスマートだと考えた次第です
raccy

2015/07/31 21:03

LINQのGroupByみたいにストリームを直接グルーピングできたら まだ少しはましだったのかも知れませんけど、Java8にはないみたいですね。
guest

0

集合の中の部分集合毎単位で処理を記述したい

ということであれば元のストリームから目的の要素を(filterで)抜き出したストリームを作ればいいのでは?

java

1IntStream grade0 = stream.filter((n) -> EvaluationRule.classify(n) == 0);

合計などが欲しくなったら分けたストリームを統合すればいいでしょう。


思いっきりC#のノリで書いてしまいましたが、
javaのStreamは使い回しができないので
一々ストリーム元からストリームを生成する必要がありました。

java

1int[] source = { 1,2,3,4,5}; 2IntStream s1 = Arrays.stream(source).filter(i -> i < 3).map(i -> 1); 3IntStream s2 = Arrays.stream(source).filter(i -> i >= 3).map(i -> 2); 4IntStream s12 = IntStream.concat(s1, s2);

投稿2015/07/31 00:31

編集2015/07/31 01:49
ozwk

総合スコア13521

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kakusuke

2015/07/31 01:15 編集

うーん、どうやら一度処理したストリームは再度処理できないようです・・・ このままでは分岐は実現できないですね・・・ IntStream stream = IntStream.of(1,2,3,4,5); IntStream s1 = stream.filter(i -> i < 3).map(i -> 1); IntStream s2 = stream.filter(i -> i >= 3).map(i -> 2); System.out.println(IntStream.concat(s1, s2).sum()); // 10 を期待 Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203) at java.util.stream.IntPipeline.<init>(IntPipeline.java:91) at java.util.stream.IntPipeline$StatelessOp.<init>(IntPipeline.java:592) at java.util.stream.IntPipeline$9.<init>(IntPipeline.java:332) at java.util.stream.IntPipeline.filter(IntPipeline.java:331) at Main.main(Main.java:7)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問