Eug*_*ene 9 java java-8 java-stream
假设我正在处理自定义收集器并假设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;
}));
Run Code Online (Sandbox Code Playgroud)
这个例子可能与此无关,但问题是:那个元素combiner可以是空的List吗?我真的很想说NO,因为在文档中这些被称为:
combiner - 一个关联的,非干扰的无状态函数,它接受两个部分结果容器并合并它们.
那么这部分对我来说是一个标志accumulator叫上他们,他们到达之前combiner,只是想确认.
Hol*_*ger 11
在合并之前,没有保证累加器已应用于容器.换句话说,要合并的列表可能是空的.
为了证明这一点:
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);
});
Run Code Online (Sandbox Code Playgroud)
在我的机器上,它打印:
0 + 0
0 + 0
0 + 0
1 + 1
0 + 2
0 + 2
1 + 1
2 + 0
2 + 2
Run Code Online (Sandbox Code Playgroud)
当尚未知道过滤操作的结果时,工作负载拆分发生在源列表中.每个块都以相同的方式处理,无需重新检查是否有任何元素已到达累加器.
请注意,从Java 9开始,您也可以执行类似的操作
IntStream.range(0, 10).parallel().boxed()
.collect(Collectors.filtering(i -> i >= 3 && i < 7, Collectors.toList()));
Run Code Online (Sandbox Code Playgroud)
这是另一个原因的集电极(在此,toList()集电体)应该准备遇到空容器,作为滤波发生在外部Stream执行和accept对化合物集电极的累加器呼叫并不总是意味着一个accept在下游收集器的累加器的呼叫.
能够处理空容器的要求在Collector文档中指定:
为确保顺序和并行执行产生相同的结果,收集器函数必须满足标识和关联约束.
标识约束表示对于任何部分累积的结果,将其与空结果容器组合必须产生等效结果.也就是说,对于作为
a任何一系列累加器和组合器调用的结果的部分累积结果,a必须等效于combiner.apply(a, supplier.get()).
| 归档时间: |
|
| 查看次数: |
249 次 |
| 最近记录: |