2017-05-03 61 views
6

,我被JDK-8望着Collectors.toSet实施和几乎看到了明显的事情: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

我不知道我理解你的问题。您已经了解了'jdk-9'实现的性能原理。为什么你会期望如果导致效率低得多的程序,你的这部法律得到维护? – gyre

+0

我不确定你的法律是否反映在这个答案中。没有指定关于折叠*左*一致,尤其是在接受的答案,这给出了有序与无序流的区别。 – gyre

+0

@gyre你可能是对的..似乎有点匆忙的问题。 – Eugene

回答

10

一个Collector的组合功能收到leftright适当,如果有遇到以维持,但是,它是达Collector,它将如何真正结合这两个参数。

documentation状态:

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

为了收集到List,那将是灾难性的,如果我们只是换left.addAll(right)right.addAll(left),但是对于一个无序Set,没关系。 toSet()收集器甚至报告UNORDERED特性暗示Stream(或任何客户端代码),即使提供的参数是leftright甚至都不重要,因此并行流可以结合任意部分结果,无论首先完成,换句话说,它可能表现得像一个无序的流,即使源有碰到命令(Java 8的实现不使用那个机会)。

至于是否它是值得的......我们是在比较单一的额外分支可能数以千计add操作就可以节约,他们每个人轴承多个条件分支内部...