2017-02-18 24 views
12

Say mam ten strumieniowe:Java 8 Streams: Collapse/abstrakcyjne strumienie części

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

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

Jak mogę zrobić to wyglądać tak:

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

bez ręcznego odwijania części FNX i zestawiając je (ze względów konserwacyjnych chcę je zachować nienaruszone i wyrazić fnPartX razem z nimi).

+3

Mapa, w przeciwieństwie do flatMap i filter, nie może zmienić liczby elementów w strumieniu. –

+1

@BenoitParis Mogłem źle zrozumieć pytanie, ale możesz przypisać część 1 do 'Stream ' i part2 do 'Stream ', a następnie zadzwonić zebrać na 'Stream '? – CKing

+3

Nie możesz. Przynajmniej nie możesz zrobić tego tak, jak chcesz (przy użyciu dwóch funkcji, używając 'map' w początkowym strumieniu). Dzieje się tak, ponieważ 'map()' działa na strumieniu * elementów * (transformuje każdy element strumienia, a nie sam strumień), więc nie można np. Filtrować elementów, które są operacjami nad samym strumieniem (usuwa niektóre elementy na podstawie warunku). –

Odpowiedz

14

Można wyrazić i komponować ją z funkcjami:

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

Typy wejściowe i wyjściowe funkcji trzeba odpowiednio dopasować.

ten może być podstawą do bardziej złożonych kompozycji skonstruować takie jak:

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

ten może być stosowany tak:

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

Że 'Kompozytor' może być bardziej ogólny, dzięki czemu można go używać z więcej niż tylko strumieniami. Przypomina mi "Opcjonalnie". – 4castle

+4

@ 4castle Masz rację. "Kompozytor" nie używa żadnych właściwości specyficznych dla strumienia - ja go edytowałem. Jest to tylko próba przeniesienia składu funkcji do struktury, która wygląda podobnie do tego, co chce OP. – Calculator

+0

Dziękujemy! Dokładnie to, czego szukałem. Martwiłem się, że muszę zmaterializować to, co pomiędzy nimi, ale wtedy pozwala nam komponować funkcje. – BenoitParis

5

Musisz użyć flatMap nie map. Nie wiem, jakie są Twoje typy są tak ja nazywa je T1, T2, itd

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

Oczywiście można usunąć niektóre operacje strumieniowe:

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

Dalsze uproszczenie jest możliwe ponieważ fnPart1 i fnPart2 mają po prostu do czynienia z pojedynczymi elementami.

Powiązane problemy