2015-03-25 18 views
8

Jak mogę policzyć dopasowania filtra strumienia? Próbuję byłaby następujący kod do java8 stream:Jak liczyć dopasowania w filtrze strumienia?

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

Jak mogę teraz dostać liczba filtrowanych elementów, które zostały zastosowane? Zapytanie: W starym kodzie mogę wielokrotnie używać Integer id. Jak mogę osiągnąć to samo w przypadku strumieni?

+1

dlaczego nie dodać zmiennej zewnętrznej jako licznika i po prostu dodać operację przyrostu w 'forEach'? – nikis

+1

@nikis Nie działałoby to ze zmienną lokalną, ponieważ zmienne przechwycone ze środowiska, z którego korzystasz w lambda, muszą być efektywne. Więc byłoby to bardziej kłopotliwe niż myślisz. – Jesper

+1

@Jesper @nikis można użyć instancji 'LongAdder' i dalej ją zwiększać. Ta klasa została dodana do Java 8 tylko w tym celu. – dkatzel

Odpowiedz

16

Od setValue jest funkcją efektem ubocznym, można użyć peek:

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

nie jestem fanem tego podejścia, ponieważ szczyt jest przeznaczona do wykorzystania na cel debugowania (będzie to rade) . Zauważ, że w Javie 9, count() może nie być w stanie wykonać potok strumienia, jeśli może on obliczyć liczbę bezpośrednio ze źródła (nie sądzę, że tak jest w tym przypadku, ponieważ stosujesz filtrowanie, ale dobrze jest mieć to na uwadze).

Zapytanie poboczne: w starym kodzie mogę ponownie użyć identyfikatora liczb całkowitych wielokrotnych razy. Jak mogę osiągnąć to samo w przypadku strumieni?

To zależy od przypadków użycia, ponieważ API nie ma krotki Twoja najlepsza szansa jest, aby utworzyć klasę, powiedzmy Tuple2, tak że można mapować każdy węzeł do nowej krotki i ponowne id .

Coś jak:

.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 

W twoim przypadku, jeśli pobyt w strumieniu węzłów, można po prostu chwycić tożsamości ze getId() w dowolnym momencie.

+0

Dzięki za wgląd. Będę wtedy trzymał się "starego" podejścia, ponieważ tego rodzaju efekty uboczne prawdopodobnie nie są przeznaczone do użycia w strumieniu api. – membersound

+3

@membersound Jeśli wiesz, co robisz, to nie jest problem. Nie zawsze można uniknąć efektów ubocznych, ponieważ Java i tak nie jest językiem funkcjonalnym (nawet z wieloma nowymi funkcjami). –

+1

Prawdopodobnie można podzielić zadanie na dwie różne operacje strumieniowe: Pierwsza ustawia identyfikator za pomocą forEach(), druga zlicza węzły za pomocą count(). – isnot2bad