2015-06-09 21 views
8

buduję obiekt o prostej pętli:budowniczy z Java 8 Stream

WebTarget target = getClient().target(u); 

for (Entry<String, String> queryParam : queryParams.entrySet()) { 
    target = target.queryParam(queryParam.getKey(), queryParam.getValue()); 
} 

chcę zrobić to samo przy użyciu API Java8 Stream, ale nie mogę dowiedzieć się, jak to zrobić. Zmusza mnie to do tego, że cel jest przydzielany za każdym razem, więc proste .forEach() nie będzie działać. Myślę, że potrzebuję użyć .collect() lub reduce(), ponieważ szukam jednej wartości zwrotu, ale jestem zagubiony w tej chwili!

Odpowiedz

6

Nie ma niestety żadnej metody foldLeft w strumieniu API. Powodem tego jest wyjaśnione przez Stuart Marks w this answer:

[...] Wreszcie, Java nie zapewnia foldLeft i foldRight operacji, ponieważ implikuje konkretną kolejność operacji, która jest z natury sekwencyjna. Jest to sprzeczne z zasadą opisaną powyżej, polegającą na zapewnieniu interfejsów API obsługujących równoczesną i równoległą obsługę.

Ostatecznie to, co próbujesz zrobić, jest czymś proceduralnym/sekwencyjnym, więc nie sądzę, że strumień API jest odpowiedni dla tego przypadku użycia. Myślę, że każda pętla, którą napisałeś, jest tak dobra, jak tylko się da.

Aktualizacja:

Jak @TagirValeev zaznacza below ty może w rzeczywistości go rozwiązać ze strumieniem API (przy użyciu forEachOrdered Twój kod będzie wtedy wyglądać

WebTarget[] arr = { getClient().target(u) }; 
queryParams.entrySet() 
      .stream() 
      .forEachOrdered(e -> arr[0] = arr[0].queryParam(e.getKey(), 
                  e.getValue())); 
WebTarget target = arr[0]; 

stoję. przez moją oryginalną odpowiedź i twierdzą, że twoje dobre stare pętle for to lepsze podejście w tym przypadku.

+0

Jestem nerwowy o tym ostatnim sumator, nawet jeśli dodano 'sekwencyjny()'. –

+1

Ja też. Nie jestem pewien, czy to prawda. Bez żadnego odpowiednika 'foldLeft', wątpię, aby strumień API był odpowiedni w tej sytuacji. – aioobe

+2

Myślę, że wolałbym odpowiedź, która wprost stwierdza, że ​​strumienie naprawdę nie wspierają tego dobrze. –

8

To n ot bardzo trudne do wykonania poprawnego foldLeft dla Java 8 potoków:

@SuppressWarnings("unchecked") 
public static <T, U> U foldLeft(Stream<T> stream, 
           U identity, BiFunction<U, ? super T, U> accumulator) { 
    Object[] result = new Object[] { identity }; 
    stream.forEachOrdered(t -> result[0] = accumulator.apply((U) result[0], t)); 
    return (U) result[0]; 
} 

albo w sposób bezpieczny typu:

public static <T, U> U foldLeft(Stream<T> stream, 
           U identity, BiFunction<U, ? super T, U> accumulator) { 
    class Box { 
     U value; 
     Box(U value) { this.value = value; } 
    } 
    Box result = new Box(identity); 
    stream.forEachOrdered(t -> result.value = accumulator.apply(result.value, t)); 
    return result.value; 
} 

To działa prawidłowo dla sekwencyjnych i równoległych strumieni. Możesz nawet uzyskać wzmocnienie szybkości przy użyciu strumieni równoległych, jeśli twój strumień zawiera kilka nieoperacyjnych operacji pośrednich bez obciążenia procesora, takich jak map: w tym przypadku następny element może być przetwarzany przez map w równoległym z bieżącym elementem przetworzonym przez foldLeft. Nie zgadzam się, że taka operacja nie jest odpowiednia dla Stream API, ponieważ można ją poprawnie wyrazić za pośrednictwem już istniejącego forEachOrdered.

mam tę operację w moim StreamEx bibliotece, więc można go używać tak:

WebTarget target = EntryStream.of(queryParams).foldLeft(getClient().target(u), 
     (t, entry) -> t.queryParam(entry.getKey(), entry.getValue())) 
+1

Nice. Mimo to twoje 3 linie kodu strumienia wydają mi się bardzo skomplikowane, w porównaniu do pętli OP's for. "Pojemnik" Object [] "nie wygląda na idiomatyczny lambda, może się mylę. – aioobe

+3

AtomicReference może być użyty zamiast tego, jeśli chcesz mieć bezpieczeństwo typu, choć nie jestem pewien, czy spowodowałoby to powstanie lambda idiomatycznego: AtomicReference result = new AtomicReference <> (identity); stream.forEachOrdered (t -> result.updateAndGet (u -> accumulator.apply (u, t)); return result.get(); – srborlongan

+1

@aioobe: implementacja 'foldLeft' jest częścią kodu biblioteki niskiego poziomu, więc dla mnie jest ok, jeśli nie wygląda zbyt ładnie, patrz na przykład klasa "Collectors": jest tam wiele podobnych rzeczy, dla bezpieczeństwa typu można utworzyć klasę dodatkową, jak 'class Box {Wartość U; Box (U v) {value = v;}} '. –