2015-03-25 13 views
8

Come posso contare le corrispondenze di un filtro di flusso? Sto cercando di refactoring il seguente codice per java8 stream:Come contare le corrispondenze su un filtro dello stream?

//java7 
int i = 0; 
for (Node node : response.getNodes()) { 
    Integer id = node.getId(); 
    if (id != null) { 
     node.setContent("This is the id: " + id); 
     i++; 
    } 
} 

//java8 
response.getNodes().stream() 
    .filter(node -> node.getId() != null) 
    .forEach(node -> node.setValue("This is the id: " + node.getId())); 

Come posso ora ottenere il conteggio di elementi filtrati che sono stati applicati? Sidequestion: nel vecchio codice posso riutilizzare lo Integer id più volte. Come posso ottenere lo stesso con i flussi?

+1

perché non aggiungere una variabile esterna come contatore e aggiungere semplicemente l'operazione di incremento in "forEach"? – nikis

+1

@nikis Questo non funzionerebbe con una variabile locale, perché le variabili catturate dall'ambiente che si utilizza in una lambda devono essere effettivamente definitive. Quindi sarebbe più complicato di quanto pensi. – Jesper

+1

@Jesper @nikis è possibile utilizzare un'istanza 'LongAdder' e continuare a incrementarla. Quella classe è stata aggiunta a Java 8 proprio per questo scopo. – dkatzel

risposta

16

Dal setValue è una funzione effetto collaterale, è possibile utilizzare peek:

long i = response.getNodes() 
       .stream() 
       .filter(node -> node.getId() != null) 
       .peek(node -> node.setValue("This is the id: " + node.getId())) 
       .count(); 

io non sono un fan di questo approccio perché picco è pensato di utilizzare per il debug scopo (questo sarebbe fare il trucco) . Si noti che in Java 9, count() potrebbe non essere in grado di eseguire la pipeline dello stream se può calcolare il conteggio direttamente dalla sorgente (non credo che sia il caso qui, poiché si applica un filtro ma è bene tenerlo a mente).

Sidequestion: nel vecchio codice posso riutilizzare l'intero numero di più volte. Come posso ottenere lo stesso con i flussi?

Dipende dal vostro caso d'uso, dal momento che l'API non ha tuple la tua migliore possibilità è quello di creare una classe, diciamo Tuple2, in modo che è possibile mappare ogni nodo ad una nuova tupla e riutilizzare l'id .

Qualcosa di simile:

.stream().map(node -> new Tuple2<>(node, node.getId()).moreStreamOps(...); 
                ^
                 | 
       at that point you have a Stream<Tuple2<Node, Integer>> 
       from which you can grab the id with Tuple2#getSecond 

Nel tuo caso, se si rimane con un flusso di nodi, si può semplicemente prendere l'ID con getId() in qualsiasi momento.

+0

Grazie per l'intuizione. Seguirò quindi il "vecchio" approccio poiché questi tipi di effetti collaterali non sono probabilmente pensati per l'uso con stream api. – membersound

+3

@membersound Beh, se sei a conoscenza di ciò che stai facendo non è un problema. Non è sempre possibile evitare gli effetti collaterali in quanto Java non è comunque un linguaggio funzionale (anche con le sue numerose nuove funzionalità). –

+1

Probabilmente si potrebbe suddividere l'attività in due operazioni di flusso differenti: la prima imposta l'id usando forEach(), la seconda conta i nodi usando count(). – isnot2bad

Problemi correlati