2016-05-06 56 views
7

由於我使用流很多,其中一些處理大量的數據,我認爲這是一個好主意,預先分配我的基於集合的收集器的大小,以防止昂貴的重新分配,因爲集合增長。所以,我想出了這個,對於其他集合類型相似的:儘可能在收集器中使用Characteristics.UNORDERED很重要嗎?

public static <T> Collector<T, ?, Set<T>> toSetSized(int initialCapacity) { 
    return Collectors.toCollection(()-> new HashSet<>(initialCapacity)); 
} 

像這樣來使用

Set<Foo> fooSet = myFooStream.collect(toSetSized(100000)); 

我擔心的是,Collectors.toSet()實施設置一個Characteristics的枚舉Collectors.toCollection()不會:Characteristics.UNORDEREDCollectors.toCollection()沒有方便的變化來設置超出默認值的所需特性,並且由於可見性問題,我無法複製Collectors.toSet()的實現。因此,設置UNORDERED特徵我被迫做這樣的事情:

static<T> Collector<T,?,Set<T>> toSetSized(int initialCapacity){ 
    return Collector.of(
      () -> new HashSet<>(initialCapacity), 
      Set::add, 
      (c1, c2) -> { 
       c1.addAll(c2); 
       return c1; 
      }, 
      new Collector.Characteristics[]{IDENTITY_FINISH, UNORDERED}); 
} 

因此,這裏是我的問題:1。 這是我爲作爲自定義簡單的事情創建一個無序收集唯一選擇toSet() 2.如果我想讓它理想地工作,是否需要應用無序特性?我讀過a question on this forum,在那裏我瞭解到無序特性不再反向傳播到Stream中。它仍然有用嗎?

+0

但是'HashSet'是無序的(所以它是一個集合的定義)。所以你的代碼片段應該仍然有任意的順序。或者我在這裏錯過了什麼? – Obicere

+2

這是個好問題,@Obicere。很顯然,它是一個無序集合的事實並不爲api所知,所以'特性'枚舉在收集器中用於提供額外的提示。查看'Collectors.toSet()'的源代碼。它還使用'HashSet',並故意將'UNORDERED'特性設置爲'Stream'或上游'Collector'的調用提示。 –

+3

ahh gotcha。只是看用法,似乎是必要的[在一些地方](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream /ReduceOps.java#185)。所以看起來2號的答案真的取決於你是否想要小的優化選項。 – Obicere

回答

4

首先,CollectorUNORDERED特性有助於提高性能,而不是其他。 Collector沒有這個特性,但不依賴於遇到順序沒有問題。

這個特性是否有影響取決於流操作本身和的實現細節。雖然目前的實現可能不會從中獲得太多優勢,但由於反向傳播的困難,這並不意味着未來版本不會。當然,已經無序的流不受CollectorUNORDERED特性的影響。並非所有的流操作都有可能從中受益。

因此,更重要的問題是如何防止這種潛在的優化(可能在未來)是多麼重要。

請注意,還有其他未指定的實現細節,在涉及到第二個變體時會影響潛在的優化。 toCollection(Supplier)收集器具有未指定的內部工作方式,只能保證提供由Supplier生成的類型的最終結果。相反,Collector.of(() -> new HashSet<>(initialCapacity), Set::add, (c1, c2) -> { c1.addAll(c2); return c1; }, IDENTITY_FINISH, UNORDERED)準確地定義了收集器應該如何工作,並且還可能阻礙內部優化收集未來版本的收集器。

所以一種指定特徵而不涉及Collector其他方面的方法是最好的解決方案,但據我所知,現有的API沒有簡單的方法。但是你自己建造這樣的設施很容易:

public static <T,A,R> Collector<T,A,R> characteristics(
         Collector<T,A,R> c, Collector.Characteristics... ch) { 
    Set<Collector.Characteristics> o = c.characteristics(); 
    if(!o.isEmpty()) { 
     o=EnumSet.copyOf(o); 
     Collections.addAll(o, ch); 
     ch=o.toArray(ch); 
    } 
    return Collector.of(c.supplier(), c.accumulator(), c.combiner(), c.finisher(), ch); 
} 

用這種方法,很容易說,

HashSet<String> set=stream 
    .collect(characteristics(toCollection(()->new HashSet<>(capacity)), UNORDERED)); 

或提供您的工廠方法

public static <T> Collector<T, ?, Set<T>> toSetSized(int initialCapacity) { 
    return characteristics(toCollection(()-> new HashSet<>(initialCapacity)), UNORDERED); 
} 

這將限制提供您的特性(如果這是一個反覆出現的問題)所需的工作量,所以它不會傷害到他們提供的,即使你不知道它會有多大的影響。

相關問題