2015-03-24 18 views
5

Chcę iterować zagnieżdżone listy przy użyciu java8 streams i wyodrębnić niektóre wyniki z list na pierwszym dopasowaniu. Niestety muszę również uzyskać wartości z treści nadrzędnej, jeśli element podrzędny pasuje do filtru.Jak iterować zagnieżdżone dla pętli odwołujących się do elementów nadrzędnych za pomocą strumieni Java 8?

Jak mogę to zrobić?

// java7

Result result = new Result(); 

//find first match and pupulate the result object. 
for (FirstNode first : response.getFirstNodes()) { 
    for (SndNode snd : first.getSndNodes()) { 
     if (snd.isValid()) { 
      result.setKey(first.getKey()); 
      result.setContent(snd.getContent()); 
      return; 
     } 
    } 
} 

// java8

response.getFirstNodes().stream() 
     .flatMap(first -> first.getSndNodes()) 
     .filter(snd -> snd.isValid()) 
     .findFirst() 
     .ifPresent(???); //cannot access snd.getContent() here 
+1

możliwy duplikat [Java 8 - Streams zagnieżdżony ForEach z inną kolekcją] (http://stackoverflow.com/questions/25357043/java-8-streams-nested-foreach- with-different-collection) –

Odpowiedz

10

Kiedy trzeba obie wartości i chcą korzystać flatMap (wymagane, gdy chcesz przeprowadzić operację zwarciem jak findFirst), trzeba mapować obiekt trzymając obie wartości

response.getFirstNodes().stream() 
    .flatMap(first->first.getSndNodes().stream() 
    .map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd))) 
    .filter(e->e.getValue().isValid()) 
    .findFirst().ifPresent(e-> { 
    result.setKey(e.getKey().getKey()); 
    result.setContent(e.getValue().getContent()); 
    }); 

Aby używać wyłącznie klas standardowych, używam Map.Entry jako typu Pair, podczas gdy prawdziwy typ Pair może wyglądać bardziej zwięźle.

W tym konkretnym przypadku zastosowania, można przejść operację filtra do strumienia wewnętrznego

response.getFirstNodes().stream() 
    .flatMap(first->first.getSndNodes().stream() 
    .filter(snd->snd.isValid()) 
    .map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd))) 
    .findFirst().ifPresent(e-> { 
    result.setKey(e.getKey().getKey()); 
    result.setContent(e.getValue().getContent()); 
    }); 

który ma estetyczny efekt, że tylko do jednego elementu dopasowującego, instancja Map.Entry zostanie utworzony (dobrze, powinno być jako the current implementation is not as lazy as it should, ale nawet wtedy nadal będzie tworzyć mniejsze obiekty niż w pierwszym wariancie).

+0

Dzięki za wyjaśnienia. Patrząc na twój kod, uważam, że lepiej jest trzymać się stylu iteracyjnego w stylu Java w przypadku, gdy trzeba uzyskać dostęp do wielu elementów nadrzędnych. – membersound

+2

Tak, stare dobre pętle 'for' nie są nieaktualne. Może przyszła wersja Java z obsługą języków dla par/tupli otwiera możliwość lepszego rozwiązania strumienia ... – Holger

-1

Powinno być tak:

Edit: Dzięki Holger za wskazanie, że kod nie zatrzyma się na pierwszy prawidłowy FirstNode

response.getFirstNodes().stream() 
    .filter(it -> {it.getSndNodes().stream().filter(SndNode::isValid).findFirst(); return true;}) 
    .findFirst() 
    .ifPresent(first -> first.getSndNodes().stream().filter(SndNode::isValid).findFirst().ifPresent(snd -> { 
    result.setKey(first.getKey()); 
    result.setContent(snd.getContent()); 
    })); 

Test można znaleźć here

+2

To nie robi " t zrobić cokolwiek pożytecznego. Oryginalny kod zapytania o nazwie 'result.setKey (first.getKey()); result.setContent (snd.getContent()); 'i zatrzymał przetwarzanie kolejnych elementów. Twój kod nic takiego nie robi. – Holger

+0

Pokazałem, jak uzyskać dostęp do 'snd.getContent()' wewnątrz 'ifPresent()', zmodyfikowałem kod teraz – vsnyc

+0

@ Hgger mogę uzyskać -1 wyłączone, przetestowałem, że mój kod działa i robi to co op przeznaczone – vsnyc

Powiązane problemy