Stream API wprowadzono w Javie 8. Umożliwia opakowywanie danych i wykonywanie na nich różnych operacji.
O jego podstawowych zastosowaniach przeczytasz tutaj: link. Przejdźmy do bardziej zaawansowanych zagadnień.
Na potrzeby przykładów skorzystamy z takiego produktu:
![]()
![]()
![]()
I takiej listy produktów:
![]()
![]()
![]()
Poniżej przekonwertujemy strumień zawierający obiekty Product na strumień zawierający obiekty Product.Type. Następnie wypiszemy na konsolę:
![]()
![]()
![]()
Oto wynik:
![]()
![]()
![]()
Collect
Po przetworzeniu strumienia chcemy zwrócić obiekty w postaci innej struktury danych. Np. Map lub Set. Do tego posłuży nam funkcja .collect().
Korzysta ona z interfejsu Collector. Ten zbudowany jest z czterech metod:
supplier – tworzy i zwraca nowy kontener np. .collect(Collectors.toList()) tworzy nam nową listę,
accumulator – agreguje dane do utworzonego kontenera np. .collect(joining()),
combiner – przyjmuje dwa wyniki i łączy je. Stosowany w strumieniach równoległych, łączy cząstkowe rezultaty,
finisher – wykonuje transformację pośrednich akumulacji do ostatecznego rezultatu.
W poniższym przykładzie filtrujemy naszą listę produktów pod kątem ceny, ponieważ wykorzystujemy interfejs funkcyjny Predicate. Służy do wykonywania operacji logicznych. Zwraca typ boolean:
![]()
![]()
![]()
Stworzyliśmy nową przefiltrowaną listę produktów. Podobnie możemy utworzyć Set. Dlatego wystarczy, że zastosujemy .collect(Collectors.toSet()).
toMap()
Możemy też dokonać transformacji strumienia na Map. Dlatego wystarczy określić klucz oraz wartość do której ma się odnosić. Oczywiście klucze w mapie są unikalne:
![]()
![]()
![]()
Metoda .limit() ogranicza nam wielkość do dwóch elementów. Tworzymy mapę. Klucz to id produktu. Wartość to obiekt produktu. Wywołujemy Collectors.toMap(). Dlatego pierwszy argument przyjmuje funkcje, która ma wyciągnąć id produktu.
Drugi argument przyjmuje funkcję, która ma wyciągnąć cały produkt. Dlatego wywołujemy Function.identity(). Funkcja zawsze zwraca wejściowy argument. W tym przypadku to cały obiekt Product. Używamy zamiast pisać np. product -> product.
Możemy też dodatkowo dodać obsługę w przypadku próby wstawienia obecnego już klucza w mapie, ponieważ tutaj rzucamy wyjątkiem IllegalStateException.
Grupowanie elementów w Stream API
Collectors umożliwia też nam grupowanie elementów strumienia. Po jakiejś wybranej wartości. W poniższym przykładzie grupowane są produkty pod względem typu produktu. Dlatego tworzy się mapa. Kluczami są wszystkie typy produktów. Wartościami listy różnych produktów o jednym określonym typie:
![]()
![]()
![]()
Możemy również podzielić kolekcję wejściową poprzez funkcję grupującą. Tutaj: counter.getAndIncrement()/size. Ta funkcja zapewnia podzielenie jednej listy na mniejsze listy o wielkości size.
W przykładzie poniżej wielkość size jest jeden. Mamy trzy produkty w głównej liście. Dlatego otrzymujemy trzy mniejsze listy. Każda zawiera jeden produkt:
![]()
![]()
![]()
Wynik to trzy oddzielne listy. Mające tylko po jednym produkcie:
![]()
![]()
![]()
Statystyki
Możemy znaleźć się w sytuacji, gdzie chcemy otrzymać statystyki na temat produktów. Dlatego Collectors udostępnia nam taką możliwość poprzez metodę summarizingDouble():
![]()
![]()
![]()
Tutaj wynik. Mamy ilość produktów, min, max, średnią i sumę:
![]()
![]()
![]()
Reduce
Operacja redukcji łączy wszystkie elementy strumienia w jeden element wynikowy. Nie będący strumieniem. Dlatego redukcja stosowana jest np. metodach min() i max(). My skorzystamy z metody sum(). Aby obliczyć sumę cen wszystkich produktów w naszym strumieniu:
![]()
![]()
![]()
Java 9
Java 9 rozbudowała Stream API o dodatkowe narzędzia. Czyli takeWhile/dropWhile, iterate i ofNullable.
TakeWhile() i dropWhile()
TakeWhile podobnie do dropWhile pomaga w obsłudze nieskończonych strumieni. A konkretnie do ich zatrzymywania. Dlatego TakeWhile podtrzymuje strumień do zajścia określonego warunku. DropWhile wstrzymuje strumień, aż nie zajdzie zdefiniowany warunek.
Banalny przykład. Za każdym razem dodajemy literę do łańcucha znaków. Tylko dopóki łańcuch znaków będzie mniejszy od trzech. Dlatego tutaj zostaną wypisane tylko linie krótsze niż trzy znaki. Po tym strumień zamknie się:
![]()
![]()
![]()
Tutaj wyświetlą się tylko wszystkie napisy dłuższe niż trzy znaki:
![]()
![]()
![]()
iterate()
Metoda .iterate() służy do wykonywania określonej ilości iteracji strumienia. W Javie 9 wprowadzono jej trzy argumentową wersję. Dzięki temu ułatwiono iterowanie po bardziej złożonych obiektach.
Przykładowo chcemy wypisać wszystkie daty. Od początku roku do dziś. Dlatego w Javie 8 musimy użyć takiej konstrukcji:
![]()
![]()
![]()
W Javie 9 powyższy kod łatwo uprościć. Możemy dodać warunek końca iterowania już w metodzie iterate:
![]()
![]()
![]()
ofNullable()
Metoda ofNullable ułatwia obsługę wartości pustych w Streamie. Nie musimy “ręcznie” sprawdzać czy dany element jest pusty, ponieważ OfNullable automatycznie robi to za nas. Użycie: Stream.ofNullable()
Ciąg dalszy nastąpi…
Źródła:
https://geek.justjoin.it/zastosowanie-stream-api-z-java-8-przyklady/2



