Collectors.toSet 实现细节
Collectors.toSet implementation detail
我正在查看 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
便宜吗,还要考虑分支的额外开销吗?
这也违反了我关于总是向左折叠的定律...
有人可以在这里阐明一下吗?
一个Collector
的combiner函数将接收 left
和right
适当的,如果有一个相遇顺序要维护,然而,这取决于 Collector
,它将如何实际结合这两个参数。
documentation 状态:
A function that accepts two partial results and merges them. The combiner function may fold state from one argument into the other and return that, or may return a new result container.
为了收集成一个List
,如果我们只是把left.addAll(right)
换成right.addAll(left)
,那将是灾难性的,但是对于一个无序的Set
,这无关紧要. toSet()
收集器甚至报告 UNORDERED
特征以提示 Stream
(或任何客户端代码)它甚至不关心提供哪个参数作为 left
或 right
,因此并行流可以组合任意部分结果,无论先完成的是什么,换句话说,它可能表现得像无序流,即使源有遇到顺序(Java 8 的实现没有'不要利用那个机会)。
关于它是否值得......我们正在比较一个额外的分支与我们可以节省的可能数千个 add
操作,每个 轴承 多个内部条件分支…
我正在查看 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
便宜吗,还要考虑分支的额外开销吗?
这也违反了我关于总是向左折叠的定律...
有人可以在这里阐明一下吗?
一个Collector
的combiner函数将接收 left
和right
适当的,如果有一个相遇顺序要维护,然而,这取决于 Collector
,它将如何实际结合这两个参数。
documentation 状态:
A function that accepts two partial results and merges them. The combiner function may fold state from one argument into the other and return that, or may return a new result container.
为了收集成一个List
,如果我们只是把left.addAll(right)
换成right.addAll(left)
,那将是灾难性的,但是对于一个无序的Set
,这无关紧要. toSet()
收集器甚至报告 UNORDERED
特征以提示 Stream
(或任何客户端代码)它甚至不关心提供哪个参数作为 left
或 right
,因此并行流可以组合任意部分结果,无论先完成的是什么,换句话说,它可能表现得像无序流,即使源有遇到顺序(Java 8 的实现没有'不要利用那个机会)。
关于它是否值得......我们正在比较一个额外的分支与我们可以节省的可能数千个 add
操作,每个 轴承 多个内部条件分支…