コース一覧
    Java中級:Streamと関数型
    並列ストリーム

    Java中級:Streamと関数型

    Stream APIと関数型インターフェースを学び、データ処理を宣言的で再利用しやすい形に書けるようにします。

    1
    Stream API
    01. Stream を作る5分
    02. filter で絞り込む5分
    03. map で変換する5分
    04. reduce で集約する5分
    05. collect で集める5分
    06. sorted で並び替え5分
    07. distinct で重複排除5分
    08. limit と skip5分
    09. 並列ストリーム5分
    10. Stream API まとめクイズ5分
    2
    関数型インターフェース
    01. ラムダ式の基本5分
    02. メソッド参照 ::5分
    03. Function インターフェース5分
    04. Predicate インターフェース5分
    05. Consumer と Supplier5分
    06. BiFunction と 2 引数の関数5分
    07. 関数型インターフェース まとめクイズ5分

    並列ストリーム

    1 行足しただけで、速くなるはずがない

    Stream には parallel() という中間操作があります。これを挟むと、要素が分割されて複数の CPU コアで同時に処理されます。

    Java

    long hits = logs.stream()
        .parallel()
        .filter(line -> line.contains("ERROR"))
        .count();

    追加したのは 1 行だけです。それで速くなるなら、全部の Stream に付ければよさそうに思えます。ところが実際に時間を測ると、多くの場合は変わらないか、むしろ遅くなります。

    理由は、並列化そのものにも手間がかかるからです。要素を分けて、それぞれのコアに配って、終わったら結果を合わせる。この段取りに時間を使います。1 件あたりの処理が軽ければ、段取りの時間の方が大きくなります。数十件から数百件のリストを流す程度では、まず割に合いません。

    parallel() は「速くする道具」ではなく「速くなるかもしれない試し方」です。付けるかどうかは、両方の書き方で実際に時間を測ってから決めます。

    順番が保証されなくなる

    速さ以前に、動きが変わる点があります。forEach の処理される順番です。

    Java

    List.of("a", "b", "c", "d").parallelStream()
        .forEach(s -> System.out.println(s));

    これを何度か実行すると、出てくる順番が毎回変わります。分割された塊が、終わった順に処理されるからです。並んだ順に出したいなら forEachOrdered を使いますが、そうすると順番を待つ分だけ並列にした意味が薄れます。

    collect(Collectors.toList()) のように結果を集める終端操作なら、元の順番は保たれます。順番が崩れて困るのは、処理の途中で表示や書き込みをしている場合です。

    共有している変数を書き換えると壊れる

    もう 1 つ、こちらの方が深刻です。

    Java

    List<String> found = new ArrayList<>();
    logs.parallelStream()
        .filter(line -> line.contains("ERROR"))
        .forEach(line -> found.add(line)); // 危ない

    ArrayList は、複数のスレッドから同時に追加されることを想定していません。同時に add が走ると、要素が入らずに消えたり、例外が出たりします。しかもいつも壊れるわけではなく、たまに壊れます。テストでは通って本番で落ちる、いちばん厄介な種類のバグです。

    結果を集めたいときは、自分で用意したリストに詰めるのではなく collect を使います。collect は並列でも安全に集まるように作られています。

    解説

    直列で書いてあるコードに parallel() を足すだけで壊れる、ということは、元のコードがラムダの外側を書き換えていたということです。並列化を試すのは、そういう書き方をしていないか点検する良い機会にもなります。

    やってみよう

    Solution.parallelSum(int[] arr) を完成させて、配列の合計を返してください。

    ファイルの先頭に import java.util.Arrays; を書き、Arrays.stream(arr) で IntStream を作ります。そこに .parallel() を挟んでから、.sum() で合計を取ります。

    今回の目的は、並列にしても答えが変わらないことを確かめることです。テストの配列は数件しか無いので、速くはなりません。むしろ直列より時間がかかります。合計のように「どこで区切って足しても答えが同じ」になる計算だから、分けて足して後から合わせても結果が一致する、というところを見てください。

    要件

    1. ファイル先頭で import java.util.Arrays; を書くこと
    2. Arrays.stream(arr) で IntStream を作り、.parallel() で並列モードに切り替えること
    3. 最後に .sum() を呼んで合計の int を return すること

    ヒント

    `Arrays.stream(int[])` は `IntStream` を返すので、`Stream<Integer>` ではなく `IntStream` 専用の `sum()` がそのまま使えます

    `.parallel()` を呼ぶ位置はパイプラインの途中ならどこでも OK。今回は `Arrays.stream(arr).parallel().sum()` の順で書くのが最短です

    `sum` は結合的 (`(a+b)+c == a+(b+c)`) なので、並列で配列をどう分割しても結果は変わりません。直列の `Arrays.stream(arr).sum()` と同じ値が返ります

    生田 陸人
    監修生田 陸人
    ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
    編集 ゆめさく編集部·公開 2026/05/19·更新 2026/08/26

    関連レッスン

    • Stream API まとめクイズ

      Stream API 章で学んだ生成 / filter / map / reduce / collect / sorted / distinct / limit / skip / parallel を、メソッドチェーンの順序と中間操作・ターミナル操作の使い分けで総ざらいする 4 択クイズ。

    • ラムダ式の基本

      Java 8 から導入されたラムダ式の文法と関数型インターフェースの考え方を学び、Stream と組み合わせて配列の合計を一行で書く感覚を身につけよう。

    • limit と skip

      Stream API の中間操作 limit と skip を使い、要素を先頭から数件だけ取り出したり、先頭の数件を飛ばしたりする方法を学んで、配列のスライス処理を Stream で書いてみよう。

    • distinct で重複排除

      Stream の `distinct()` で配列やリストから重複要素を取り除き、ユニークな要素数を集計する書き方を身につけよう。

    このレッスンに出てくる用語

    意味があいまいなまま進んだ語は、ここから読み直せます。

    • 処理計算や代入を表す長方形
    • リスト順序付きで複数の値を扱うデータ構造
    • 変数データに名前をつけて参照する仕組み
    • スレッド1 つの質問に紐づく返信の集まり
    • テストバグを見つける工程
    • 配列サイズ固定の同型データの集まり
    main.java
    エディタを読み込んでいます

    メモ

    並列ストリーム

    ⌘S で保存