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

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

ただいまの
回答率

90.35%

  • Java

    16745questions

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

Streamを途中で分岐させたい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 8,567

kakusuke

score 73

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

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

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

ちなみに、途中で終端処理を挟まない方法を模索中です
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+4

実現したいのは、下記のようなデーターフローでしょうか?
[Source] --> [FilterX] --> [Split] --> [FilterA] --> [SinkA]
                              +------> [FilterB] --> [SinkB]
残念ながら、Java8のストリーム(java.util.stream.Stream)は、汎用のデータフロー・パイプライン処理のための仕組みではありません。出力先が2個以上あるような「分岐」は表現不可能です。

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/01 00:29

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

    キャンセル

+1

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

public class SumPoint2 {
  public static void main(String[] args) {
    Stream<Integer> stream = Stream.of(1, 2, 3, 60, 65, 85, 90, 92, 95);
    Integer sum = stream.collect(Collectors.groupingByConcurrent((n) -> {
        if (n >= 90) {
          return "A";
        } else if (n >= 80) {
          return "B";
        } else if (n >= 60) {
          return "C";
        } else {
          return "Z";
        }
      })).entrySet().parallelStream().mapToInt((e) -> {
        switch(e.getKey()) {
          case "A":
            return e.getValue().stream().mapToInt(n -> 3 + (n - 90)).sum();
          case "B":
            return 2 * e.getValue().size();
          case "C":
            return e.getValue().size();
        }
        return 0;
      }).sum();

    System.out.println(sum); // => 20
  }
}
--------
以下は普通にする、修正前の回答です。

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

public class SumPoint {
  public static void main(String[] args) {
    IntStream stream = IntStream.of(1, 2, 3, 60, 65, 85, 90, 92, 95);
    Integer sum = stream
      .map((n) -> {
        if (n >= 90) {
          return 3 + (n - 90);
        } else if (n >= 80) {
          return 2;
        } else if (n >= 60) {
          return 1;
        } else {
          return 0;
        }
      })
      .sum();
    System.out.println(sum); // => 20
  }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/07/31 08:59

    ご回答ありがとうございます

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

    キャンセル

  • 2015/07/31 19:23

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

    キャンセル

  • 2015/08/01 00:40

    説明が足りずすみません、、、

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

    キャンセル

  • 2015/08/01 06:03

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

    キャンセル

0

集合の中の部分集合毎単位で処理を記述したい
ということであれば元のストリームから目的の要素を(filterで)抜き出したストリームを作ればいいのでは?

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

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


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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/07/31 10:14 編集

    うーん、どうやら一度処理したストリームは再度処理できないようです・・・
    このままでは分岐は実現できないですね・・・

    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)

    キャンセル

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

  • ただいまの回答率 90.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Java

    16745questions

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