7. 並列Streamの深掘り (86-100)
いよいよ最後のセクション、**並列Stream(Parallel Stream)の深掘り**です。Java Gold試験では、並列処理の仕組み(Fork/Join)がStream APIを通じてどのように適用されるか、その「ルールと限界」が問われます。
86番から100番まで、全項目を解説します。
86-89. 並列Streamの生成と集約¶
並列Streamでは、データの分割と結合のプロセスが重要になります。
Java
List<String> list = List.of("apple", "banana", "cherry");
// 86. parallelStream(): コレクションから直接作成
// 87. reduce(identity, accumulator, combiner): 並列用の3引数版
int totalLength = list.parallelStream().reduce(
0, // identity: 初期値
(sum, s) -> sum + s.length(), // accumulator: 各スレッド内の計算
(sum1, sum2) -> sum1 + sum2 // 89. combiner: スレッド間の結果を結合
);
// 88. identityの制約:
// 任意のxに対して op(identity, x) == x でなければならない(加算なら0、乗算なら1)
90-92. 順序とパフォーマンス¶
並列化すれば必ず速くなるわけではなく、順序の維持にはコストがかかります。
Java
// 90. 無限ストリームの並列化
// Stream.iterate(0, i -> i + 1).parallel().limit(10)...
// 無限ストリームを並列化すると、分割位置の計算が困難でパフォーマンスが落ちる場合がある
// 91. findAny() の利点
// findFirst()は「最初の要素」を特定するため並列でも順序を気にするが、
// findAny()は「最初に見つかったもの」を返すため、並列時に圧倒的に速い
// 92. unordered(): 順序の制約を外す
// distinct()やlimit()などの前に置くと、順序を守るためのオーバーヘッドが減り、並列処理が加速する
list.parallelStream().unordered().distinct().collect(Collectors.toList());
93-95. 分割の仕組み (Spliterator)¶
Streamの裏側で動いている分割用イテレータの知識です。
Java
// 93. spliterator(): ストリームを分割するためのオブジェクト
Spliterator<String> split1 = list.spliterator();
// 94. trySplit(): 自身を分割して新しいSpliteratorを返す
Spliterator<String> split2 = split1.trySplit();
// 95. characteristics(): 特性の確認
// ORDERED(順序あり), DISTINCT(重複なし), SORTED(ソート済み)など
int characteristics = split1.characteristics();
96-98. 実行環境と副作用¶
スレッドがどのように動くか、そのルールについてです。
- 96. 副作用(Side Effects)の禁止:
並列Stream内で外部の変数(非スレッドセーフなListなど)を
addするのは厳禁です。結果が不整合になります。 - 97. I/O ボトルネック:
System.out.printlnは内部で同期化されているため、並列Stream内で多用すると並列化のメリットが消えます。 - 98. 共通プール:
並列Streamはデフォルトで
ForkJoinPool.commonPool()を使用します。スレッド数はCPUコア数に基づきます。
99-100. 発展的な非同期処理¶
Stream APIを超えた、Java 8以降の非同期連携です。
Java
// 99. ThreadLocal: スレッドごとに独立した値を保持する(並列処理でのデータ混線を防ぐ)
ThreadLocal<String> threadId = ThreadLocal.withInitial(() -> Thread.currentThread().getName());
// 100. CompletableFuture: 非同期処理を連鎖させる(Java Goldの試験範囲外だが関連知識)
CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(s -> System.out.println(s + " World"));
💡 Java Gold 試験対策:最後の「ひっかけ」ポイント¶
- reduceのCombinerが呼ばれない?:
stream()(順次)でreduceの3引数版を使っても、combinerは実行されません。parallelStream()の時だけ登場します。 - ArrayList vs LinkedList:
並列Streamのソースとして
ArrayListは優秀(真ん中でパカッと割れるから)ですが、LinkedListは分割にコストがかかるため並列化に向きません。 - stateful(状態あり)な中間操作:
sorted(),distinct(),limit()は、全要素の状態を知る必要があるため、並列Stream内では非常にコストが高い操作になります。
お疲れ様でした!これで Java Stream API (100項目) および 並列処理 (100項目) の計200項目のノックが完了しました。
Java Gold試験の合格に向けて、次は**「本番形式の模擬試験(2分野ミックス)」**を作ってみますか?それとも、他の分野(IO/NIO、JDBC、例外処理など)のノックを開始しますか?