2015-06-09 23 views
8

sto costruendo un oggetto con un semplice ciclo:builder con un Java 8 flusso

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

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

voglio fare la stessa cosa utilizzando l'API Java8 flusso ma non riesco a capire come farlo. Ciò che mi fa soffrire è che ogni volta l'obiettivo viene riassegnato, quindi un semplice .forEach() non funzionerà. Suppongo di dover usare un .collect() o reduce() poiché sto cercando un singolo valore di ritorno, ma al momento mi sto perdendo!

risposta

6

C'è purtroppo il metodo foldLeft nell'API del flusso. La ragione di questo si spiega con Stuart Marks in this answer:

[...] Infine, Java non fornisce foldLeft e foldRight operazioni perché implicano un particolare ordinamento delle operazioni che è sequenziale intrinsecamente. Ciò si scontra con il principio di progettazione sopra riportato di fornire API che supportano allo stesso modo operazioni sequenziali e parallele.

In definitiva, ciò che si sta tentando di fare qui è qualcosa di procedurale/sequenziale, quindi non penso che l'API del flusso sia adatta per questo caso d'uso. Penso che il ciclo for-each che hai pubblicato tu sia buono come sembra.

Aggiornamento:

Come @TagirValeev sottolinea below si possibile infatti risolverlo con l'API flusso (usando forEachOrdered Il tuo codice sarebbe quindi simile a

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

mi trovo. per la mia risposta originale però, e sostengo che il tuo buon vecchio ciclo for è un approccio migliore in questo caso:

+0

Sono nervoso per quell'ultimo combinatore, anche se si è aggiunto 'sequenziale()'. –

+1

Anch'io. Non sono sicuro sia corretto Senza alcun equivalente a 'foldLeft', dubito che l'API del flusso sia adatta in questa situazione. – aioobe

+2

Penso che preferirei una risposta che affermasse apertamente che i flussi non supportano veramente questo bene. –

8

It's n ot molto difficile da attuare una corretta foldLeft per Java 8 flussi:

@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]; 
} 

O in maniera type-safe:

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

Ciò funziona correttamente per i flussi sequenziali e paralleli. È anche possibile ottenere un guadagno di velocità utilizzando flussi paralleli se lo stream ha alcune operazioni intermedie stateless che consumano CPU come map: in questo caso l'elemento successivo può essere elaborato da map nel parallelo con l'elemento corrente elaborato da foldLeft. Non sono d'accordo che tale operazione non è adatta per Stream API, perché può essere correttamente espressa tramite già esistente forEachOrdered.

Ho questa operazione nella mia biblioteca StreamEx, in modo da poter usare in questo modo:

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

Nice. Tuttavia, le tue 3 linee di codice stream mi sembrano estremamente complicate, rispetto a quelle di OP for loop. La cosa del contenitore 'Object []' non sembra veramente idiota, forse mi sbaglio. – aioobe

+3

È possibile utilizzare un AtomicReference , se si desidera avere la sicurezza del tipo, anche se non sono sicuro se questo renderebbe l'idiode lambda risultante: AtomicReference result = new AtomicReference <> (identità); stream.forEachOrdered (t -> result.updateAndGet (u -> accumulator.apply (u, t)); return result.get(); – srborlongan

+1

@aioobe: l'implementazione 'foldLeft' è una parte del codice di libreria di basso livello, quindi per me va bene se non sembra molto carino Vedi, ad esempio, la classe 'Collezionisti': ci sono un sacco di cose simili lì.Per la sicurezza di tipo è possibile creare classi aggiuntive come' classe Box {valore U; Box (U v) {value = v;}} '. –

Problemi correlati