2014-06-14 11 views
7

in Java 8, betrachten java.util.stream.Stream#forEach als Ersatz für traditionelle for-Schleife. Aber warum nicht das ist nicht Kettenfunktion. Sagt, dass es void statt Stream<T> es selbst zurückgibt.Warum nicht Kette java.util.stream.Stream # forEach

Gefallen Sie diesen

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

Die erste ist schön, als zweiten one.But der erste schlechte Performance.

Also, warum ist forEach nicht verkettbar?

Antwort

11

Da forEach ist eine Terminal-Operation. Es zwingt den Strom, alle seine Elemente zu verbrauchen und den Verbraucher für jeden von ihnen anzurufen. Sobald dies erledigt ist, wurde der Stream verbraucht und kann nicht wiederverwendet werden.

Ein Strom hat so viele Zwischenoperationen, wie Sie wollen, kann aber nur ein Terminalbetrieb haben.

18

Die Methoden, die Sie für exist suchen und Stream::peek und Stream::map genannt. Mit Stream::peek könnte der obige Code wie folgt aussehen.

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

Beachten Sie jedoch, dass in 'peek Nebenwirkungen()' oder 'Karte()' Verhaltensparameter, die mit der Quelle oder stören könnten Auswirkungen auf das Ergebnis anderer Stream-Operationen, ist ein No-No. Die 'peek()' Methode ist wirklich nur zum Debuggen/Loggen da, nicht um das Ergebnis zu berechnen; In ähnlicher Weise sollte 'map()' nur zum Transformieren und nicht zum Verbergen von Nebenwirkungen verwendet werden. –

5

Der forEach Betrieb ist in der Tat ein Terminalbetrieb und ist somit nicht verkettbar, aber es ist möglich, mehrere Operationen (Verbraucher, in diesem Fall) in einen einzigen forEach Anruf zu komponieren. Zum Beispiel:

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

Leider scheint es nicht möglich, die Lambdas inline ohne eine Reihe von hässlichen Casting schreiben.

2

mit andThen Chaining ist in der Tat der Weg zu gehen, aber wir brauchen etwas syntaktischen Zucker es ziemlich zu machen:

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

Das uns erlaubt, zu tun:

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 (kürzer):

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

(seit forEach verfügbar ist direkt auf dem colle ction)

3

Fortsetzung von Stuart Marks' answer, ist es möglich, die Lambda-Ausdrücke inline zu schreiben, aber nur der erste Verbraucher muss gegossen werden:

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

Jedes Mal, wenn du eine explizite Besetzung machst, stirbt eine Fee. – marthursson

0

Wie andThen definiert in Java 8 als

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

Sie können ganz einfach Ihr eigenes Composite Consumer<T> für eine beliebige Anzahl Ihrer Funktionen erstellen.

und es für eine beliebige Anzahl Ihrer Funktionen auf einmal

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