2017-05-03 102 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操作就可以節約,他們每個人軸承多個條件分支內部...