2016-01-28 6 views
8

Ho cercato di capire e mostrare come i flussi Java implementano un tipo di fusione loop sotto il cofano, in modo che più operazioni possano essere fuse in un singolo passaggio.Operazione di streaming Java e operazioni intermedie stateful

Questo primo esempio qui:

Stream.of("The", "cat", "sat", "on", "the", "mat") 
     .filter(w -> { 
      System.out.println("Filtering: " + w); 
      return w.length() == 3; 
     }) 
     .map(w -> { 
      System.out.println("Mapping: " + w); 
      return w.toUpperCase(); 
     }) 
     .forEach(w -> System.out.println("Printing: " + w)); 

ha il seguente output (con la fusione di un singolo passaggio per ogni elemento chiaro):

Filtering: The 
Mapping: The 
Printing: THE 
Filtering: cat 
Mapping: cat 
Printing: CAT 
Filtering: sat 
Mapping: sat 
Printing: SAT 
Filtering: on 
Filtering: the 
Mapping: the 
Printing: THE 
Filtering: mat 
Mapping: mat 
Printing: MAT 

Il secondo esempio è lo stesso ma utilizzare l'operazione Sort() tra il filtro e la mappa:

Stream.of("The", "cat", "sat", "on", "the", "mat") 
     .filter(w -> { 
      System.out.println("Filtering: " + w); 
      return w.length() == 3; 
     }) 
     .sorted() 
     .map(w -> { 
      System.out.println("Mapping: " + w); 
      return w.toUpperCase(); 
     }) 
     .forEach(w -> System.out.println("Printing: " + w)); 

Questo ha il seguente output:

Filtering: The 
Filtering: cat 
Filtering: sat 
Filtering: on 
Filtering: the 
Filtering: mat 
Mapping: The 
Printing: THE 
Mapping: cat 
Printing: CAT 
Mapping: mat 
Printing: MAT 
Mapping: sat 
Printing: SAT 
Mapping: the 
Printing: THE 

Quindi la mia domanda è qui, con la chiamata a distinti, sono io ragione nel pensare che, poiché si tratta di un'operazione intermedia "stateful", che non consente singoli elementi da elaborare singolarmente nel corso di un singolo passaggio (di tutte le operazioni). Inoltre, poiché l'operazione stateful sort() deve elaborare l'intero flusso di input per produrre un risultato, la tecnica di fusione non può essere distribuita qui, quindi è per questo che tutto il filtraggio si verifica prima e quindi unisce le operazioni di mappatura e stampa , dopo il tipo? Per favore correggimi se qualcuno dei miei presupposti non è corretto e sentiti libero di approfondire ciò che ho già detto.

Inoltre, come può decidere in base a quale limite fondere insieme gli elementi in un singolo passaggio o meno, ad esempio, quando esiste l'operazione distinct(), c'è semplicemente una bandiera che si spegne per fermarlo da succede come quando distinto() non c'è?

Una query finale è, mentre il vantaggio di operazioni di fusione in un singolo passaggio è talvolta evidente, ad esempio, quando combinato con cortocircuito. Quali sono i principali vantaggi di unire insieme operazioni come filter-map-forEach o persino una filter-map-sum?

+0

potresti semplicemente rispondere alle tue domande eseguendo il debug del tuo esempio – wero

+1

Immagino che ogni volta che hai scritto "distinto" intendessi "ordinato" ... – Holger

risposta

11

stateless (mappa, filtro, flatMap, peek, ecc.) Sono completamente fusi; costruiamo una catena di oggetti in cascata Consumer e inseriamo i dati. Ogni elemento può essere gestito indipendentemente l'uno dall'altro, quindi non c'è mai nulla di "bloccato" nella catena. (Questo è ciò Louis intende per come la fusione è implementato -. Componiamo le fasi in un grande funzione, e inserire i dati a quello)

Stateful operazioni (distinto, filtrate, limite, ecc) sono più complicati e varia di più nel loro comportamento. Ogni operazione di stato può scegliere come implementare se stessa, quindi può scegliere l'approccio meno intrusivo possibile.Ad esempio, distinct (in alcune circostanze), consente agli elementi di uscire come sono controllati, mentre sorted è una barriera completa. (La differenza sta in quanto pigrizia è possibile e quanto bene gestiscono cose come infinite fonti con un'operazione limite a valle.)

È vero che le operazioni con stato generalmente minano alcuni dei benefici della fusione, ma non tutte (le operazioni a monte ea valle possono ancora essere fuse)

Oltre al valore del cortocircuito, che hai osservato, ulteriori grandi vincite dalla fusione includono (a) non è necessario popolare i contenitori dei risultati intermedi tra le fasi e (b) i dati con cui si ha a che fare sono sempre "caldi" nella cache.

+0

Grazie per la risposta - molto hellpful. Ho ragione nel ritenere che senza la fusione, il cortocircuito e la valutazione lenta non sarebbero possibili nell'API Stream? – Tranquility

+0

Non è in bianco e nero. Ci sono casi in cui non è ancora possibile cortocircuitare, anche se teoricamente potremmo, e ci sono casi che potremmo ancora cortocircuitare senza fusione. Ma certamente facilita un cortocircuito più efficace. –

+0

È un altro vantaggio della fusione il fatto che, con qualcosa di semplice come una sequenza di elementi, per esempio 10.000.000 di elementi o più, che richiedono il filtraggio e la mappatura, lo stream può iniziare a stamparli immediatamente piuttosto che dover attendere tutti i filtri e la mappatura finire? Inoltre, presumo che la fusione si presti al parallelismo? – Tranquility

4

Sì, è giusto. Tutto ciò può essere controllato guardando il codice sorgente.

Fusion non è implementato nel modo in cui penso che tu pensi che sia, però. Non si guarda l'intero oleodotto e si decide come fonderlo; non ci sono bandiere o altro; è solo se le operazioni sono espresse come un oggetto StatefulOp, che può eseguire l'intero flusso fino a quel punto e ottenere tutto l'output, oppure un StatelessOp che decora semplicemente un Sink che dice dove vanno gli elementi. Puoi guardare il codice sorgente per es. sorted e map per esempi.