並行リダクション用Collectorでcombiner関数の実装は必須?
Java8のSteam APIで並行リダクション(concurrent reduction)をサポートした自作Collectorを作るとき、Collectorのcombiner関数が呼ばれることはないと仮定しても良いものでしょうか?
(実効的な意味はない)下記サンプルコードでは、自作CollectorはCONCURRENT
かつUNORDERED
特性を持っており:
- 並列ストリーム(
parallel()
あり)なら並列リダクション = supplier関数があるスレッド上で1回呼ばれる+各Workerスレッドから並行にaccumulator関数が呼ばれる - 逐次ストリーム(
parallel()
なし)なら単一スレッド上のリダクション = supplier関数+accumulator関数が単一スレッドから呼ばれる
と期待されるため、いずれのケースでもcombiner関数が使われる事は無いと考えられます。
import java.util.stream.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class DoesConcReductionCollectorNeedsCombiner {
static public void main(String[] args) {
ConcurrentMap<Integer, AtomicInteger> result =
IntStream.range(1, 10_000_001).boxed() // Stream<Integer>
.parallel() // 並列ストリーム化
.collect(Collector.of(
() -> new ConcurrentHashMap<>(),
(m,v) -> {
AtomicInteger a = m.putIfAbsent(v % 10, new AtomicInteger(v));
if (a != null)
a.getAndAdd(v);
},
(m,n) -> {
// 並行リダクションなら呼び出されない? = 実装不要?
// (まじめに実装するなら2つの並行Mapのマージが必要)
System.out.println("!");
return null;
},
Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED
));
System.out.println("r="+result);
}
}