2015-05-03 12 views
7

Napisałem ten kod, aby zmniejszyć listę słów do dużej liczby słów zaczynających się na "A". Piszę to tylko, żeby nauczyć się Java 8, więc chciałbym to trochę lepiej zrozumieć [Uwaga: zdaję sobie sprawę, że to prawdopodobnie nie jest najlepszy sposób napisania tego kodu; to tylko ćwiczenie!].Java8 stream.reduce() z 3 parametrami - uzyskanie przezroczystości

Long countOfAWords = results.stream().reduce(
    0L, 
    (a, b) -> b.charAt(0) == 'A' ? a + 1 : a, 
    Long::sum); 

środkowy parametr/lambda (zwany akumulator) wydaje się być zdolne do zmniejszenia pełną listę bez końcowego parametru „sumator”. W rzeczywistości, Javadoc rzeczywiście mówi:

Opcja {} function @code akumulator działa jako skondensowanego odwzorowującym i akumulatorze, *, która czasami może być bardziej wydajny niż oddzielnym mapowania i redukcji * przykład gdy wiedząc poprzednio zmniejszona wartość pozwala uniknąć pewnych obliczeń na *.

[Edit od autora] - Poniższe oświadczenie jest źle, więc nie pozwól, by mylić; Po prostu trzymam to tutaj, więc nie niszczę pierwotnego kontekstu odpowiedzi.

W każdym razie mogę wywnioskować, że akumulator musi po prostu wyprowadzać 1 i 0, które łączy kombinator. Jednak nie znalazłem tego szczególnie oczywistego z dokumentacji.

Moje pytanie

Czy istnieje sposób, aby zobaczyć, jakie wyjście byłoby zanim wykona sumator więc mogę zobaczyć listę 1 i 0, że kombajny sumator? Byłoby to pomocne w debugowaniu bardziej złożonych sytuacji, które na pewno napotkam w końcu.

+1

Dlaczego nie po prostu 'long countOfAWords = results.stream(). Filter (x-> x.charAt (0) == 'A'). Count();'? – Holger

+0

Zdecydowanie mógłbym, ale po prostu bawiłem się nowymi mechanizmami dla doświadczenia; to nie był prawdziwy kod :) –

Odpowiedz

8
Kombinator nie zmniejsza listy 0 i 1. Gdy strumień nie jest prowadzony równolegle nie jest stosowany w tym przypadku tak, że następująca pętla jest równoważne:

U result = identity; 
for (T element : this stream) 
    result = accumulator.apply(result, element) 
return result; 

Po uruchomieniu strumień równolegle, zadanie jest rozpięta na wiele wątków. Na przykład dane w potoku są podzielone na porcje, które oceniają i tworzą wynik niezależnie. Następnie łącznik służy do łączenia tych wyników.

Nie zobaczysz listy, która jest zmniejszona, ale dwie wartości: wartość tożsamości lub inna wartość obliczona przez zsumowane zadanie.Na przykład jeśli dodasz oświadczenie druku w sumator

(i1, i2) -> {System.out.println("Merging: "+i1+"-"+i2); return i1+i2;}); 

można zobaczyć coś takiego:

Merging: 0-0 
Merging: 0-0 
Merging: 1-0 
Merging: 1-0 
Merging: 1-1 

byłoby to pomocne w debugowania bardziej złożonych sytuacjach, które Jestem pewien, że napotkasz na razie.

Bardziej ogólnie, jeśli chcesz zobaczyć dane w rurociągu w podróży, możesz użyć peek (lub może pomóc także debugger). Więc stosowane do np:

long countOfAWords = result.stream().map(s -> s.charAt(0) == 'A' ? 1 : 0).peek(System.out::print).mapToLong(l -> l).sum(); 

który może wyjście:

100100 

[Zastrzeżenie: Zdaję sobie sprawę, prawdopodobnie nie jest to najlepszy sposób, żeby napisać ten kod ; to tylko ćwiczenie!].

idiomatyczne sposób, aby osiągnąć swoje zadanie byłoby filter strumień, a następnie po prostu użyć count:

long countOfAWords = result.stream().filter(s -> s.charAt(0) == 'A').count(); 

Nadzieję, że to pomaga! :)

+3

To jedna z najlepszych odpowiedzi na pytanie, jakie kiedykolwiek miałem na pytanie :) Dzięki. –

+2

@ JohnHumphreys-w00te Wow, dziękuję bardzo :-) Cieszę się, że odpowiedział na twoje pytanie! –

2

Jednym ze sposobów sprawdzenia, co się dzieje, jest zastąpienie metody o numerze referencyjnym Long::sum przez lambdę zawierającą println.

List<String> results = Arrays.asList("A", "B", "A", "A", "C", "A", "A"); 
Long countOfAWords = results.stream().reduce(
     0L, 
     (a, b) -> b.charAt(0) == 'A' ? a + 1 : a, 
     (a, b) -> { 
      System.out.println(a + " " + b); 
      return Long.sum(a, b); 
     }); 

W tym przypadku widzimy, że łącznik nie jest faktycznie używany. Jest tak dlatego, że strumień nie jest równoległy. Wszystko, co naprawdę robimy, to używanie akumulatora do kolejnego łączenia każdego String z bieżącym wynikiem Long; żadne dwie wartości nie są nigdy łączone.

Po zamianie stream przez parallelStream widać, że używany jest łącznik i spójrz na wartości, które łączy.

+0

Dziękuję również za odpowiedź; to było zdecydowanie łatwe w użyciu w mojej sytuacji. –