2014-06-14 11 views
7

In java 8, java.util.stream.Stream#forEach considerare come sostituzione per il ciclo tradizionale. Ma perché non si tratta di una funzione a catena. Restituisce non valida invece di Stream<T> autonomamente.Perché non concatenare java.util.stream.Stream # forEach

Ti piace questa

Arrays 
    .stream(Girls.toArray()) 
    .forEach(Girls::getUp) 
    .forEach(Girls::dressUp) 
    .filter(/* Top 10 Girls */) 
    .forEach(Gay.getMe()::gotGirl) 
    .endFilter()// Not an API, but it means remove last filter 
    .filter(/* Worst 10 Girls */) 
    .forEach(Gay.get(0)::gotGirl) 


girls = Arrays 
    .stream(Girls.toArray()); 
girls.forEach(g->{g.getUp();g.dressUp()}); 
girls.filter(/* Top 10 Girls */) 
    .forEach(Gay.getMe()::gotGirl); 
girls.filter(/* Worst 10 Girls */) 
    .forEach(Gay.get(0)::gotGirl); 

Il primo è bello che secondo one.But il primo ha ottenuto performance peggiore.

Quindi, perché forEach non è concatenabile?

risposta

11

Perché forEach è un'operazione terminale. Costringe il flusso a consumare tutti i suoi elementi e chiama il consumatore per ciascuno di essi. Fatto ciò, lo stream è stato consumato e non può essere riutilizzato.

Un flusso ha tutte le operazioni intermedie che si desidera, ma può avere solo un'operazione di terminale.

18

I metodi che stai cercando esistono e sono chiamati Stream::peek e Stream::map. Con Stream::peek, il codice sopra riportato potrebbe apparire come segue.

Arrays 
    .stream(Girls.toArray()) 
    .peek(Girls::getUp) 
    .peek(Girls::dressUp) 
    .filter(/* Top 10 Girls */) 
    .peek(Gay.getMe()::gotGirl) 
    ... 
+10

Tuttavia, si noti che hanno effetti collaterali in 'peek()' o 'mappa()' parametri comportamentali che possono interferire con la fonte, o influire sul risultato di altre operazioni di streaming, è un no-no. Il metodo 'peek()' è davvero lì solo per il debug/logging, non per calcolare il risultato; allo stesso modo, 'map()' dovrebbe essere usato solo per la trasformazione, non per nascondere gli effetti collaterali. –

5

Il forEach operazione è infatti un'operazione terminale e non è pertanto chainable, ma è possibile comporre diverse operazioni (consumatori, in questo caso) in un unico forEach chiamata. Ad esempio:

Consumer<String> c1 = s -> System.out.println(s + "1"); 
    Consumer<String> c2 = s -> System.out.println(s + "2"); 
    Consumer<String> c3 = s -> System.out.println(s + "3"); 
    Arrays.asList("a", "b", "c") 
      .stream() 
      .forEach(c1.andThen(c2).andThen(c3)); 

Sfortunatamente non sembra possibile scrivere le lambda in linea senza un brutto lancio.

2

Concatenamento con andThen è davvero la strada da percorrere, ma abbiamo bisogno di un po 'di zucchero sintattico per renderlo abbastanza:

public class Consumers { 
    public static <T> Consumer<T> of(Consumer<T> base) { 
     return base; 
    } 
} 

Questo ci permette di fare:

Arrays.asList("a", "b", "c") 
     .stream() 
     .forEach(Consumers.of(s -> System.out.println(s + "1")) 
         .andThen(s -> System.out.println(s + "2")) 
         .andThen(s -> System.out.println(s + "3"))); 

Or (più breve):

Arrays.asList("a", "b", "c") 
     .forEach(Consumers.of(s -> System.out.println(s + "1")) 
         .andThen(s -> System.out.println(s + "2")) 
         .andThen(s -> System.out.println(s + "3"))); 

(dal forEach è disponibile direttamente sul colle ction)

3

Proseguendo da Stuart Marks' answer, è possibile scrivere la linea lambda, ma solo bisogno di essere lanciato il primo consumatore:

Arrays.asList("a", "b", "c").forEach(
    ((Consumer<String>) s -> System.out.println(s + "1")) 
    .andThen(s->System.out.println(s + "2")) 
    .andThen(s -> System.out.println(s + "3")) 
); 
+2

Ogni volta che fai un cast esplicito muore una fata. – marthursson

0

Come andThen è definito in Java 8 come

default Consumer<T> andThen(Consumer<? super T> after) { 
    Objects.requireNonNull(after); 
    return (T t) -> { accept(t); after.accept(t); }; 
} 

puoi facilmente creare il tuo composito

per qualsiasi numero di funzioni

e usarlo per qualsiasi numero di funzioni in una sola volta

List<Consumer<String>> funcs = ... 
Arrays.asList("a", "b", "c") 
     .forEach(new CompositeConsumer(funcs));