2015-07-25 25 views
18

Mam następujący kod i chciałbym go zaimplementować za pomocą funkcji lambda dla zabawy. Czy można to zrobić za pomocą podstawowych operacji agregujących?Jak wykonać zagnieżdżone instrukcje "jeśli" używając Java 8/lambda?

List<Integer> result = new ArrayList<>(); 

for (int i = 1; i <= 10; i++) { 
    if (10 % i == 0) { 
     result.add(i); 
     if (i != 5) { 
      result.add(10/i); 
     } 
    } 
} 

Korzystanie lambda:

List<Integer> result = IntStream.rangeClosed(1, 10) 
           .boxed() 
           .filter(i -> 10 % i == 0) 
           // a map or forEach function here? 
           // .map(return 10/i -> if i != 5) 
           .collect(Collectors.toList()); 

Odpowiedz

35

Istotną obserwacją jest to, że Twój problem polega na non-izomorficzne transformację: pojedynczy element wejściowy mogą odwzorowywać na zero, jeden lub dwa elementy wyjściowe. Ilekroć to zauważasz, powinieneś natychmiast zacząć szukać rozwiązania, które obejmuje flatMap zamiast map, ponieważ jest to jedyny sposób na osiągnięcie takiej ogólnej transformacji. W danym przypadku można najpierw zastosować filter dla elementu mapowanie jeden do zera, następnie flatMap do mapowania jeden do dwóch:

List<Integer> result = 
    IntStream.rangeClosed(1, 10) 
      .filter(i -> 10 % i == 0) 
      .flatMap(i -> i == 5 ? IntStream.of(i) : IntStream.of(i, 10/i)) 
      .boxed() 
      .collect(toList()); 
+1

Podoba mi się, jak nie tylko odpowiedzieć na pytanie, ale także nauczyć się myśleć o problemie, aby dostać się do rozwiązania. – marcus

4

Można zadeklarować ciała dla lambda. Na przykład:

Runnable run =() -> System.out.println("Hey"); 

Może być

Runnable run =() -> { 
    System.out.println("Hey"); 
}; 

ramach tego organu, można tworzyć zagnieżdżone oświadczenia:

Runnable run =() -> { 
    int num = 5; 

    if(num == 5) { 
     System.out.println("Hey"); 
    } 
}; 
+0

ale czy można to zrobić za pomocą 'map',' filter', etc? Próbuję poznać podstawy funkcji lambda. Dzięki. – LuckyGuess

+0

@ Z-1 Nie jestem pewien, czy filtry są w stanie to zrobić, ale składnia byłaby '.filter (i -> {return yourLogic;})' – RAnders00

+0

@ Z-1 Wyrażenie lambda powstaje w wyniku użycia * funkcjonalny interfejs * (interfejs z tylko jedną 'abstrakcyjną' metodą, taką jak 'Runnable'; może mieć wiele' domyślnych' metod). Na przykład "Wątek" akceptuje 'Runnable' w jego konstruktorze. Możemy napisać 'nowy wątek (() -> {});'. Możesz nawet tworzyć własne funkcjonalne interfejsy. Aby odpowiedzieć "* możliwe do zrobienia za pomocą' map' i 'filter' *": ** yes **. Działa dla * wszystkich * lambd. –

0

Spróbuj użyć flatMap:

List<Integer> result = IntStream.rangeClosed(1, 10) 
          .boxed() 
          .flatMap((i) -> { 
           List<Integer> results = new ArrayList<>(); 
           if (10 % i == 0) { 
            results.add(i); 
            if (i != 5) { 
             results.add(10/i); 
            } 
           } 
           return results.stream(); 
          }) 
          .collect(Collectors.toList()); 

Zobacz http://ideone.com/EOBiEP

1

Można to zrobić:

 List<Integer> result1 = IntStream 
     .rangeClosed(1, 10) 
     .boxed() 
     .filter(i -> 10 % i == 0) 
     .map(i -> (i != 5 ? Stream.of(i, 10/i) : Stream.of(i))) 
     .flatMap(Function.identity()) 
     .collect(Collectors.toList()); 
3

Zastosowanie flatMap jak próbujesz dodać elementy do rurociągu lub Mapowanie 1-do-wielu. Mapa to mapowanie jeden do jednego.

ArrayList<Integer> result = (ArrayList<Integer>) IntStream.rangeClosed(1, 10) 
       .boxed() 
       .filter(i -> 10 % i == 0) 
       .flatMap((Integer i) -> {return i!=5 ? Stream.of(i, (10/i)):Stream.of(i);}) 
       .collect(Collectors.toList()); 

Skutkuje to tym samym liście jako

ArrayList<Integer> result2 = new ArrayList<Integer>(); 

     for (int i = 1; i <= 10; i++) { 
      if (10 % i == 0) { 
       result2.add(i); 
       if (i != 5) { 
        result2.add(10/i); 
       } 
      } 
     } 

W przypadku, gdy zastanawiasz się, w jaki sposób jest szybsza metoda pętla jest ~ 3 razy szybciej niż przy użyciu strumieni.

Benchmark      Mode Cnt  Score  Error Units 
testStreams.Bench.loops  avgt 5  75.221 ± 0.576 ns/op 
testStreams.Bench.streams  avgt 5 257.713 ± 13.125 ns/op 
+1

To jest interesujące. Dzięki za benchmark. Zauważyłem też, że strumienie są znacznie wolniejsze niż tradycyjne pętle. – LuckyGuess

+0

Zależnie od aplikacji naprawdę, operacje na intach są jednymi z najprostszych operacji, jakie możesz zrobić, ponieważ to, co robiłeś, powoduje, że obciążenie podczas konfigurowania strumienia jest zbyt wysokie. Znalazłem pomocną drugą odpowiedź na ten [ten post] (http://stackoverflow.com/questions/27925954/is-arrays-streamarray-name-sum-slower-than-iterative-approach/27994074#27994074). – Mantis

+0

strumień nadmiarowy rzeczywiście zmniejsza się wraz ze wzrostem zasięgu pętli. nie całkiem jednak znika w tym przypadku. – the8472

Powiązane problemy