2017-02-18 3 views
12

Sagen, ich habe diesen Stream:Java 8 Streams: Collapse/abstract Bäche Teile

list.stream() 
    .map(fnPart1)  
    .map(fnPart2) 
    .collect(Collectors.toList()) 

Ohne manuell Abwickeln der FNX Teile und setzen sie zusammen:

list.stream() 
    .map(fn1)  // part1 
    .map(fn2)  // 
    .filter(fn3) // 

    .flatMap(fn4) // part 2 
    .map(fn5)  // 
    .filter(fn6) // 
    .map(fn7)  // 
    .collect(Collectors.toList()) 

Wie kann ich es so aussehen zu lassen (Aus Wartungsgründen möchte ich sie unberührt lassen und die FnPartX mit ihnen ausdrücken).

+3

Karte, im Gegensatz zu FlatMap und Filter, kann die Anzahl der Elemente im Stream nicht ändern. –

+1

@BenoitParis Ich habe die Frage vielleicht missverstanden, aber Sie könnten part1 einem 'Stream ' und part2 einem 'Stream ' zuweisen und dann collect auf dem 'Stream ' aufrufen? – CKing

+3

Sie können nicht. Zumindest können Sie es nicht so machen, wie Sie wollen (mit zwei Funktionen, mit 'map' im ursprünglichen Stream). Das liegt daran, dass 'map()' auf den stream * elements * funktioniert (es transformiert jedes Element des Streams, nicht den Stream selbst), so dass Sie zB keine Elemente filtern können, was eine Operation über den Stream selbst ist einige Elemente basieren auf einer Bedingung). –

Antwort

14

Sie könnten ausdrücken und komponieren sie mit Funktionen:

Function<Stream<T1>, Stream<T2>> fnPart1 = 
     s -> s.map(fn1) 
      .map(fn2) 
      .filter(fn3); 
Function<Stream<T2>, Stream<T3>> fnPart2 = 
     s -> s.flatMap(fn4) 
      .map(fn5)  
      .filter(fn6) 
      .map(fn7); 

fnPart1.andThen(fnPart2).apply(list.stream()).collect(Collectors.toList()); 

Die Eingangs- und Ausgangstypen der Funktionen entsprechend anzupassen.

Dies kann die Grundlage für eine komplexere Zusammensetzung konstruieren wie:

public class Composer<T>{ 
    private final T element; 

    private Composer(T element){ 
     this.element = element; 
    } 

    public <T2> Composer<T2> andThen(Function<? super T, ? extends T2> f){ 
     return new Composer<>(f.apply(element)); 
    } 

    public T get(){ 
     return element; 
    } 

    public static <T> Composer<T> of(T element){ 
     return new Composer<T>(element); 
    } 
} 

Dies kann wie folgt verwendet werden:

Composer.of(list.stream()) 
    .andThen(fnPart1) 
    .andThen(fnPart2) 
    .get() 
    .collect(Collectors.toList()); 
+3

Das 'Composer' könnte wahrscheinlich generischer sein, so dass es mit mehr als nur Streams verwendet werden kann. Es erinnert mich an 'Optional'. – 4castle

+4

@ 4castle Sie haben Recht. Der 'Composer' verwendet keine streamspezifischen Eigenschaften - ich habe ihn bearbeitet. Es ist nur ein Versuch, die Zusammensetzung der Funktionen in eine Struktur zu bringen, die ähnlich aussieht wie das OP. – Calculator

+0

Danke! Genau das, was ich gesucht habe. Ich machte mir Sorgen, dass ich das Ding zwischendurch materialisieren müsste, aber dann können wir Funktionen komponieren. – BenoitParis

5

Sie haben flatMap nicht Karte zu verwenden. Ich weiß nicht, was Ihre Typen sind so habe ich sie T1, T2 genannt, usw.

list.stream() 
    .flatMap(fnPart1)  
    .flatMap(fnPart2) 
    .collect(Collectors.toList()) 

Stream<T2> fnPart1(T1 t1) { 
    return Stream.of(t1).map(fn1).map(fn2).filter(fn3); 
} 

Stream<T3> fnPart2(T2 t2) { 
    return Stream.of(t2).flatMap(fn4).map(fn5).filter(fn6).map(fn7); 
} 

Natürlich können Sie einige der Stream-Operationen entfernen konnte:

Stream<T2> fnPart1(T1 t1) { 
    return Stream.of(fn2(fn1(t1))).filter(fn3); 
} 

Stream<T3> fnPart2(T2 t2) { 
    return fn4(t2).map(fn5).filter(fn6).map(fn7); 
} 

Eine weitere Vereinfachung ist möglich da fnPart1 und fnPart2 nur mit einzelnen Elementen zu tun haben.