何时使用Collectors.groupingByConcurrent?

ern*_*t_k 4 java java-8 java-stream

我无法了解的确切用例Collectors.groupingByConcurrent。从JavaDocs:

返回在类型T的输入元素上实现级联“ group by”操作
的并发收集器。这是一个并发且无序的收集器。
...

也许这里的关键字是级联的“ group by”。这是否表明收集器如何进行实际积累?(从源头上看,它变得非常复杂)


当我用假货测试时 ConcurrentMap

class FakeConcurrentMap<K, V> extends HashMap<K, V> 
    implements ConcurrentMap<K, V> {}
Run Code Online (Sandbox Code Playgroud)

我发现并行流会中断(由于映射不是线程安全的,因此导致错误的聚合):

Map<Integer, Long> counts4 = IntStream.range(0, 1000000)
        .boxed()
        .parallel()
        .collect(
            Collectors.groupingByConcurrent(i -> i % 10, 
                                          FakeConcurrentMap::new, 
                                          Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)

没有.parallel(),结果将始终正确。因此,似乎groupingByConcurrent并行流。

但是,据我所知,使用以下方式收集的并行流groupingBy始终会产生正确的结果:

Map<Integer, Long> counts3 = IntStream.range(0, 1000000)
        .boxed()
        .parallel()
        .collect(
            Collectors.groupingBy(i -> i % 10, 
                                  HashMap::new,
                                  Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)

因此,什么时候使用groupingByConcurrent而不是正确groupingBy(确定不能仅仅将分组作为并发映射来获取)?

Lou*_*man 5

所有收集器都可以在并行流中正常工作,但是支持直接并发(带有Collector.Characteristics.CONCURRENT)的收集器有资格进行其他人无法进行的优化。 groupingByConcurrent属于这一类。

(大致来说,发生的情况是,非并发的收集器将输入分解为每个线程,在每个线程中创建一个累加器,然后在最后合并它们。并发(无序)收集器创建一个累加器并具有多个工作线程同时将元素合并到同一累加器中。)