Collectors.toSet实现细节

时间:2017-05-03 18:38:10

标签: java java-8 java-stream java-9

我在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;这已经在here之前讨论过,但想法是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便宜,考虑分支的额外开销呢?

这也打破了我的法律总是左折......

有人可以在这里说清楚吗?

1 个答案:

答案 0 :(得分:10)

Collector的合并器功能将接收 leftright,如果有需要维护的遭遇订单,则会正常到Collector,它将如何实际组合这两个论点。

documentation州:

  

接受两个部分结果并合并它们的函数。组合器函数可以将状态从一个参数折叠到另一个参数并返回该参数,或者可以返回新的结果容器。

要收集到List,如果我们只是将left.addAll(right)交换为right.addAll(left),那将是灾难性的,但对于无序Set,这并不重要。 toSet()收集器甚至会报告UNORDERED特征,以提示Stream(或任何客户端代码),即使提供哪个参数left或{ {1}},所以并行流可以组合任意部分结果,无论先完成什么,换句话说,它可能表现得像一个无序流,即使源有一个遭遇顺序(Java 8的实现不使用那个机会) )。

关于它是否值得...我们正在比较一个额外的分支与可能保存的数千个right操作,每个承载多个内部条件分支......