2016-05-06 10 views
7

Dal momento che utilizzo molto gli stream, alcuni dei quali trattano una grande quantità di dati, ho pensato che sarebbe una buona idea pre-allocare i miei raccoglitori basati su collezioni con una dimensione approssimativa per evitare costose riallocazioni man mano che la raccolta cresce . Così mi è venuta con questo, e altri simili per altri tipi di raccolta:È importante utilizzare Characteristics.UNORDERED in Collectors quando possibile?

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

usa così

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

La mia preoccupazione è che l'attuazione di Collectors.toSet() imposta un enum Characteristics che Collectors.toCollection() non lo fa: Characteristics.UNORDERED . Non esiste alcuna variazione conveniente di Collectors.toCollection() per impostare le caratteristiche desiderate oltre il valore predefinito e non è possibile copiare l'implementazione di Collectors.toSet() a causa di problemi di visibilità. Quindi, per impostare la caratteristica UNORDERED sono costretto a fare qualcosa di simile:

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}); 
} 

Così qui sono le mie domande: 1. È questa la mia unica opzione per la creazione di un collezionista non ordinata per qualcosa di semplice come un costume toSet() 2. Se voglio che funzioni in modo ideale, è necessario applicare la caratteristica non ordinata? Ho letto a question on this forum dove ho appreso che la caratteristica non ordinata non è più propagata indietro nel flusso. Ha ancora uno scopo?

+0

Ma un 'HashSet' non è ordinato (e quindi è la definizione di un set). Quindi lo snippet di codice dovrebbe essere ancora in ordine arbitrario. O mi sto perdendo qualcosa qui? – Obicere

+2

Questa è una buona domanda, @Obicere. Apparentemente il fatto che si tratti di una collezione non ordinata non è noto all'api, quindi l'enume 'Characteristics' è usato nei collezionisti per fornire suggerimenti aggiuntivi sull'api. Vedi il codice sorgente per 'Collectors.toSet()'. Utilizza anche un 'HashSet' e imposta deliberatamente la caratteristica' UNORDERED' come suggerimento per 'Stream' o upstream' Collector' chiamandola. –

+3

ahh gotcha. Solo guardando gli usi, sembra necessario [in alcuni punti] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream /ReduceOps.java#185). Quindi sembra che la risposta al numero 2 dipenda davvero se si desidera che siano disponibili piccole opzioni di ottimizzazione. – Obicere

risposta

4

Prima di tutto, la caratteristica UNORDERED di un Collector è lì per aiutare le prestazioni e nient'altro. Non c'è niente di sbagliato con un Collector che non ha questa caratteristica ma non dipende dall'ordine dell'incontro.

Se questa caratteristica ha un impatto dipende dalle operazioni del flusso stesso e dai dettagli di implementazione . Mentre l'attuale implementazione non può trarre molto vantaggio da esso, a causa delle difficoltà con la back-propagation, non implica che le future versioni non lo faranno. Naturalmente, un flusso che è già non ordinato, non è influenzato dalla caratteristica UNORDERED dello Collector. E non tutte le operazioni di streaming hanno il potenziale per trarne vantaggio.

Quindi la domanda più importante è quanto sia importante non impedire tali potenziali ottimizzazioni (forse in futuro).

Si noti che vi sono altri dettagli di implementazione non specificati, che influiscono sulle potenziali ottimizzazioni quando si tratta della seconda variante. Il collector toCollection(Supplier) ha funzionamenti interni non specificati e garantisce solo di fornire un risultato finale del tipo prodotto da Supplier. Al contrario, Collector.of(() -> new HashSet<>(initialCapacity), Set::add, (c1, c2) -> { c1.addAll(c2); return c1; }, IDENTITY_FINISH, UNORDERED) definisce esattamente come il collector dovrebbe funzionare e può anche ostacolare le ottimizzazioni interne dei collezionisti che producono collezioni di versioni future.

Quindi un modo per specificare le caratteristiche senza toccare gli altri aspetti di un Collector sarebbe la soluzione migliore, ma per quanto ne so, non esiste un modo semplice offerto dall'API esistente. Ma è facile costruire un simile impianto da soli:

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); 
} 

con tale metodo, è facile da dire, ad esempio,

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

o fornire il metodo factory

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

Questo limita lo sforzo necessario per fornire le vostre caratteristiche (se si tratta di un problema ricorrente), in modo che non farà male a fornire loro, anche se non so quanto avrà l'impatto.

Problemi correlati