代码之家  ›  专栏  ›  技术社区  ›  Eugene

流收集累加器/组合器顺序

  •  8
  • Eugene  · 技术社区  · 7 年前

    这基本上是一个后续行动 this answer of mine .

    假设我正在研究CustomCollector,假设 accumulator 总是 会在供应商返回的集合中添加一些元素,是否有可能 combiner 调用时,其中一个中间结果将为空?一个例子可能更容易理解。

    假设我有一个 List 我想把它分成一个列表 2 是分隔符。比如我有 1, 2, 3, 4, 2, 8 ,结果应该是 [[1], [3, 4], [8]] . 这其实并不复杂(不要对代码判断太多,我写得很快,只是为了写这个问题)。

    List<List<Integer>> result = Stream.of(1, 2, 3, 4, 2, 8)
                .collect(Collector.of(
                        () -> new ArrayList<>(),
                        (list, elem) -> {
                            if (list.isEmpty()) {
                                List<Integer> inner = new ArrayList<>();
                                inner.add(elem);
                                list.add(inner);
                            } else {
                                if (elem == 2) {
                                    list.add(new ArrayList<>());
                                } else {
                                    List<Integer> last = list.get(list.size() - 1);
                                    last.add(elem);
                                }
                            }
                        },
                        (left, right) -> {
                            // This is the real question here:
                            // can left or right be empty here?
                            return left;
                        }));
    

    在本例中,这可能是不相关的,但问题是:一个 列表 ? 我真的很想说 NO ,因为在文件中这些被称为:

    组合器-一个关联的,非干扰的,无状态的函数,接受 两个部分结果容器 并将它们合并。

    部分 对我来说是一个迹象 合路器 ,但我只是想确定一下。

    1 回复  |  直到 7 年前
        1
  •  11
  •   Community CDub    5 年前

    无法保证在合并之前已将累加器应用于容器。换句话说,要合并的列表可能为空。

    IntStream.range(0, 10).parallel().boxed()
             .filter(i -> i >= 3 && i < 7)
             .collect(ArrayList::new, List::add, (l1,l2)->{
                 System.out.println(l1.size()+" + "+l2.size());
                 l1.addAll(l2);
             });
    

    0 + 0
    0 + 0
    0 + 0
    1 + 1
    0 + 2
    0 + 2
    1 + 1
    2 + 0
    2 + 2
    

    当筛选器操作的结果还不知道时,工作负载拆分会在源列表中发生。每个块都以相同的方式处理,而不必重新检查是否有任何元素已到达累加器。

    IntStream.range(0, 10).parallel().boxed()
            .collect(Collectors.filtering(i -> i >= 3 && i < 7, Collectors.toList()));
    

    这也是为什么收藏家 toList() 收集器)应准备好遇到空容器,因为过滤发生在容器外部 Stream 实施和评估 accept 调用复合收集器并不总是意味着 接受 呼叫下游收集器。

    能够处理空集装箱的要求在 Collector documentation :

    为了确保顺序执行和并行执行产生相等的结果,收集器函数必须满足 associativity 约束条件。

    a 这是一系列累加器和组合器调用的结果, 必须等于 combiner.apply(a, supplier.get()) .

    推荐文章