2017-02-03 24 views
5

Mam poniższy fragment kodu.Jak iterować zagnieżdżone dla pętli odnoszące się do elementów nadrzędnych za pomocą Java 8 Streams i Lambdas?

public static ModuleKey getDeployableModuleFromModulesList(List<Module> modules) { 
     ModuleKey deployableModuleKey = null; 
     for(Module module : modules) { 
      List<Artifact> artifacts = module.getArtifacts(); 
      for(Artifact artifact : artifacts) { 
       if(artifact.getType().equals("ear")) { 
        return module.getKey(); 
       } else if(!artifact.getType().equals("ear")) { 
        if(artifact.getType().equals("war")) { 
         deployableModuleKey = module.getKey(); 
        } 
       } 
      } 
     } 
     return deployableModuleKey; 
    } 

Kod ten znajduje pierwsze '' z deployableModuleKey key = 'ucho' czy ostatni z key = 'wojna'. Chcę uzyskać tę samą funkcjonalność co powyżej, używając strumieni Java 8 i Lambdas.

tej pory co próbowałem to:

modules.stream().flatMap(e -> e.getArtifacts().stream()) 
    .filter(e -> e.getType().equals("ear")).findFirst() 
    .orElseGet(() -> modules.stream().flatMap(e -> e.getArtifacts().stream()) 
    .filter(e -> e.getType().equals("war")).reduce((a, b) -> b).orElse(null)); 

Powyższy fragment kodu zwróci obiekt typu Artifact zamiast Module. Chcę uzyskać ten moduł, w którym artefakt pasuje do warunków. Po znalezieniu modułu mogę zwrócić klucz, wykonując module.getKey(). Aby to zrobić, chciałbym wiedzieć, w jaki sposób odsyła się elementy pętli rodzica.

Nie jestem pewien, czy mój kod Java 8 jest całkowicie poprawny.
Czy ktoś może mi pomóc w tym zakresie?

+1

Proponuję podzielić ten proces na kilka funkcji. Nie oznacza to, że strumienie Java 8 nie są zachęcane do łączenia operacji, ale do tego, aby były jak najbardziej klarowne i atomowe. –

+0

Wątpię, czy używanie strumieni jest lepsze niż rzeczywisty kod. Strumienie nie nadają się do utrzymywania stanu między operacjami pośrednimi. Dodatkowo twój aktualny kod powoduje przejście przez wszystkie artefakty, podczas gdy proponowane rozwiązania powodują dwa przejścia. Jeśli chcesz zrobić to za pomocą tylko jednego przebiegu, sugeruję utworzenie własnego kolekcjonera (jest tu wiele pytań na temat tworzenia kolektorów, wybierz jedną i kontynuuj od tego) –

Odpowiedz

0

Wymyśliłem rozwiązanie mojego problemu.

public static ModuleKey getDeployableModuleFromModulesList(List<Module> modules) { 
    Optional<ModuleKey> op = modules.stream().filter(module -> module.getArtifacts().stream().anyMatch(artifact -> artifact.getType().equals("ear"))).map(module -> module.getKey()).findFirst(); 
    if (!op.isPresent()) { 
     op = modules.stream().filter(module -> module.getArtifacts().stream().anyMatch(artifact -> artifact.getType().equals("war"))).map(module -> module.getKey()).reduce((a, b) -> b); 
    } 
    return op.orElse(null); 
} 

W każdym razie, inne rozwiązania również są mile widziane. W przypadku, gdy ktoś doda inne rozwiązanie, wypróbuję je wszystkie.

0

Rzeczą w przypadku strumieni jest, że po zmodyfikowaniu strumienia przy użyciu operacji pośredniej, takiej jak filter lub map, nie można odwołać się do wartości z poprzedniej operacji. Musisz więc znaleźć obejście, aby zawsze zachować (w twoim przypadku Module) wartość, do której chcesz się odnosić w swoim strumieniu. W poniższym rozwiązaniu otwieram dwa strumienie wtórne z operacjami anyMatch, które dają wartość dodatnią, jeśli moduł zawiera artefakt zawierający pożądany klucz.

To powinno załatwić sprawę:

ModuleKey key = Optional.ofNullable(modules.stream().filter(m -> m.getArtifacts().stream() 
    .anyMatch(a -> a.equals("ear"))).findFirst().orElse(modules.stream() 
    .filter(m -> m.getArtifacts().stream().anyMatch(a -> a.equals("war"))) 
    .reduce((a, b) -> b).orElse(null))).map(Module::getKey).orElse(null); 

Aby znaleźć pierwszy element, użyj findFirst, która zwraca Optional<T>. Łańcuch ten jest Optional z Optional#orElse, który rozpakowuje go, a jeśli jest pusty, zwraca inną wartość (w tym przypadku przy użyciu reduce((a, b) -> b), aby znaleźć ostatni element). W przypadku, gdy nic nie zostanie znalezione, nie chcemy, aby kod wyrzucał NullPointerException. Więc zanim zadzwonimy do metody getKey, zawijamy ją do Optional z Optional.ofNullable i ustawiamy domyślną wartość określoną w kodzie pod numerem orElse na null.

Na marginesie,

else if(!artifact.getType().equals("ear")) { 
    if(artifact.getType().equals("war")) { 
    } 
} 

może być zredukowana do

else if(artifact.getType.equals("war")) { 
} 
+0

Następujące błędy kompilacji pojawiają się, gdy używam fragmentu kodu . 'Filtr metody (( m) -> {}) jest niezdefiniowany dla typu Lista ' i 'Metoda getKey() jest niezdefiniowana dla typu ModuleUtil'. Metoda 'getKey()' jest częścią obiektu 'Module'. –

+0

@RITZXAVI Prototypowałem mój kod w Notatniku ++ zamiast mojego normalnego IDE i pojawiły się nowe usterki. Zmieniłem moją odpowiedź, aby je naprawić, powiedz mi, jeśli nadal masz problemy. – MikaelF

+0

Teraz pojawia się ten błąd. 'Metoda orElse (Module) w rodzaju Opcjonalnie nie ma zastosowania dla argumentów (Opcjonalnie )' –

0

myślę, że są świadomi, że zbędny if sprawdzić w swoim starym stylu kodu Java.

Napisałem twój wielki pojedynczy strumień operacji, rozpadając się na mniejsze, łatwe w obsłudze funkcje, które każdy zrozumie.

public static ModuleKey getDeployableModuleFromModulesList(List<Module> modules) { 

    return findEARKey(modules.stream()) 
      .orElse(findWARKey(modules.stream()).orElse(null)); 

} 

public static Optional<ModuleKey> findEARKey(Stream<Module> moduleStream){ 

    return moduleStream.flatMap(e -> e.getArtifacts().stream()) 
      .filter(e -> e.getType().equals("ear")) 
      .map(Artifact::getKey).findFirst(); 
} 

public static Optional<ModuleKey> findWARKey(Stream<Module> moduleStream){ 

    return moduleStream.flatMap(e -> e.getArtifacts().stream()) 
      .filter(e -> e.getType().equals("war")) 
      .map(Artifact::getKey).findFirst(); 
} 
+0

Myślę, że twój kod jest dobry, jeśli chcę "deployableModuleKey" z type = 'ear' ** LUB ** 'war'.Ale zamiast tego chcę, jest pierwszy' deployableModuleKey 'z type =' ear '. Jeśli 'ear' nie jest obecny, chcę zwrócić ostatni modułKey z type = 'war'. –

+1

Poza tym kod ten zwraca klucz artefaktowy zamiast klucza modułu –

+0

Tak, w obiekcie 'Artefakt' nie ma atrybutu o nazwie' klucz'. –