2016-05-17 15 views
11

C'è alcuna garanzia che le operazioni su un sequenziale e ordinato flusso sono elaborati al fine incontro?In Java 8, un flusso sequenziale e ordinato garantisce l'esecuzione delle operazioni nell'ordine di incontro?

Voglio dire, se ho il codice come questo:

IntStream.range(0, 5) 
     .map(i -> { 
      myFunction(i); 
      return i * 2; 
     }) 
     .boxed() 
     .collect(toList()); 

C'è una garanzia, che esso deve svolgere myFunction() chiama in ordine all'incontro del campo generato?

ho trovato draft JavaDocs for the Stream classe in cui si afferma esplicitamente questo:

Per oleodotti flusso sequenziale, tutte le operazioni sono eseguite nell'ordine incontro della fonte gasdotto, se la fonte gasdotto ha un ordine incontro definito.

ma nella riga official JavaDocs questa riga è stata rimossa. Ora discute l'ordine dell'incontro solo per i metodi selezionati. Il Package java.util.stream doc nei effetti collaterali comma precisa:

Anche quando un oleodotto è costretto a produrre un risultato coerente con l'ordine incontro della sorgente flusso (ad esempio, IntStream.range(0,5).parallel().map(x -> x*2).toArray() deve produrre [0, 2, 4, 6, 8]), senza le garanzie sono fatte sull'ordine in cui la funzione mapper è applicata ai singoli elementi, o in quale thread qualsiasi parametro comportamentale viene eseguito per un determinato elemento.

ma non dice nulla sui flussi sequenziali e l'esempio è per un flusso parallelo (La mia comprensione è che è vero per entrambi i flussi sequenziali e paralleli, ma questa è la parte che non sono sicuro).

D'altra parte, essa afferma anche nella sezione Ordering:

Se un flusso è ordinato, la maggior parte delle operazioni sono costretti ad operare sugli elementi nel loro ordine incontro; se la fonte di uno stream è un List contenente [1, 2, 3], il risultato dell'esecuzione di map(x -> x*2) deve essere [2, 4, 6]. Tuttavia, se la fonte non ha un ordine di incontro definito, qualsiasi permutazione dei valori [2, 4, 6] sarebbe un risultato valido.

ma questa volta è iniziare con "operando sugli elementi", ma l'esempio è di circa il risultante flusso, quindi non sono sicuro che stanno prendendo gli effetti collaterali in considerazione e gli effetti collaterali è davvero ciò che questo domanda riguarda

+2

Questo risponde alla tua domanda? http://stackoverflow.com/questions/36102961/what-is-the-order-in-which-stream-operations-are-applied-to-list-elements – Tunaki

+0

Ho ampliato la domanda per includere la sezione degli ordini, a me sembra che sia in contraddizione con la sezione degli effetti collaterali. – piotrek

+0

Forse questo http://stackoverflow.com/questions/29216588/how-to-ensure-order-of-processing-in-java8-streams –

risposta

1

Penso che possiamo imparare molto dal fatto che questa frase esplicita è stata rimossa. Questa domanda sembra essere strettamente correlata alla domanda “Does Stream.forEach respect the encounter order of sequential streams?”. In pratica, lo answer from Brian Goetz dice che non esiste uno scenario in cui l'ordine viene ignorato dall'attuale implementazione del flusso quando forEach viene richiamato su un flusso sequenziale, forEachha la libertà di ignorare l'ordine dell'incontro anche per i flussi sequenziali per specifica.

Consideriamo ora il seguente sezione di Stream’s class documentation:

Per eseguire un calcolo, le operazioni di flusso sono composti in un Stream Pipeline. Una pipeline di flusso è costituita da una sorgente (che potrebbe essere una matrice, una raccolta, una funzione generatore, un canale I/O, ecc.), Zero o più operazioni intermedie (che trasformano uno stream in un altro stream, ad esempio filter(Predicate)) e un'operazione terminale (che produce un risultato o un effetto collaterale, ad esempio count() o forEach(Consumer)). I flussi sono pigri; il calcolo sui dati di origine viene eseguito solo quando viene avviata l'operazione terminale e gli elementi di origine vengono utilizzati solo se necessario.

Poiché è il funzionamento del terminale che determina se gli elementi sono necessari e se sono necessari nell'ordine incontro, la libertà di un'azione terminale di ignorare l'ordine incontro anche implica consumare, quindi lavorazione, gli elementi di un ordine arbitrario .

Si noti che non solo lo forEach può farlo. A Collector è possibile segnalare una caratteristica UNORDERED, ad es. Collectors.toSet() non dipende dall'ordine dell'incontro. È ovvio che anche un'operazione come count() non dipende dall'ordine: in Java 9 può anche tornare senza alcun elemento di elaborazione. Pensa a IntStream#sum() per un altro esempio.

In passato, l'attuazione era troppo ansioso nel propagare una caratteristica non ordinato il flusso, vedere “Is this a bug in Files.lines(), or am I misunderstanding something about parallel streams?” dove il funzionamento del terminale influenzato l'esito di una fase skip, che è il motivo per cui la implementazione corrente è riluttante circa tale ottimizzazioni per evitare bug simili, ma ciò non preclude la ricomparsa di tali ottimizzazioni, quindi viene implementato con più attenzione ...

Quindi al momento è difficile immaginare come un'implementazione possa mai trarre vantaggio dalle prestazioni sfruttando la libertà di valutazioni non ordinate in un flusso sequenziale, ma, come indicato nella domanda relativa a forEach, che non implica alcuna garanzia.

+1

@Hulk: Se la somma di 'int's traboccherà o meno * non * dipende dall'ordine degli elementi. Il risultato di 'a + b + c' è lo stesso di' c + b + a' o 'b + c + a', ecc. Per tutti i valori' int', anche quando si verifica un overflow. Ecco perché ho scelto 'IntStream # sum()' piuttosto che 'DoubleStream # sum()', dove l'ordine fa effettivamente la differenza (anche se la documentazione di 'DoubleStream # sum()' afferma esplicitamente che l'ordine delle aggiunte sarà non definito). – Holger

+0

Sono giunto alla stessa conclusione dopo averci pensato un po '- ecco perché ho già cancellato i miei commenti. Ci scusiamo per l'eventuale confusione causata;) – Hulk

+0

* Quindi al momento è difficile immaginare come un'implementazione possa mai trarre beneficio dalle prestazioni sfruttando la libertà di valutazioni non ordinate in un flusso sequenziale * - '.sorted(). FindAny()' potrebbe eliminare l'ordinamento. Se la disparità può essere propagata allo splitteratore di origine (non può), allora anche l'attraversamento della struttura dei dati potrebbe essere ottimizzato. – the8472

Problemi correlati