我在查看jdk-8下的Collectors.toSet实现时,几乎看到了显而易见的事情:
public static <T> Collector<T, ?, Set<T>> toSet() {
return new CollectorImpl<>(
(Supplier<Set<T>>) HashSet::new,
Set::add,
(left, right) -> { left.addAll(right); return left; }, // combiner
CH_UNORDERED_ID);看一下combiner;这一点在这里之前已经讨论过了,但是想法是a combiner folds from the second argument into the first。这显然发生在这里。
但是,随后我查看了jdk-9实现,并看到了以下内容:
public static <T> Collector<T, ?, Set<T>> toSet() {
return new CollectorImpl<>(
(Supplier<Set<T>>) HashSet::new,
Set::add,
(left, right) -> {
if (left.size() < right.size()) {
right.addAll(left); return right;
} else {
left.addAll(right); return left;
}
},
CH_UNORDERED_ID);现在,这种情况发生的原因有点明显--添加less elements to a bigger Set, then the other way around所需的时间更少。但是这真的比普通的addAll便宜吗,考虑一下这个分支的额外开销吧?
而且这违反了我关于总是折叠左边的定律..。
有人能在这里亮点光吗?
发布于 2017-05-03 19:19:06
Collector的组合器函数将适当地接收left和right,如果存在需要维护的相遇顺序,则取决于Collector,它将如何实际组合这两个参数。
文档指出:
接受两个部分结果并将它们合并的函数。组合器函数可以将一个参数的状态折叠到另一个参数中,然后返回该参数,或者返回一个新的结果容器。
对于收集到一个List,如果我们只是把left.addAll(right)换成right.addAll(left),那将是灾难性的,但是对于无序的Set来说,这并不重要。toSet()收集器甚至报告UNORDERED特性,以向Stream (或任何客户端代码)提示,提供哪一个参数作为left或right并不重要,因此并行流可以合并任意的部分结果,不管首先完成了什么,换句话说,它的行为可能像一个无序的流,即使源有一个遇到顺序(Java8的实现没有利用这个机会)。
关于它是否值得,…我们将单个附加分支与我们可以保存的数千个add操作进行比较,每个操作在内部都包含多个条件分支(…)。
https://stackoverflow.com/questions/43767685
复制相似问题