Wrażenia z używania Stream API w Javie 8

Ostatnio zapoznałem się z API strumieni wprowadzonego w Javie 8. Wprowadzenie API w stylu funkcjonalnym do języka Java to wspaniała wiadomość – język idzie w dobrym kierunku.

Użycie jest nawet proste – najpierw tworzymy strumień:

Dla kolekcji:

kolekcja.stream()

Dla tablic:

Arrays.stream(tablica)

Dla jednej lub kilku wartości

Stream.of("aaa", "bbb", "ccc") // strumienie obiektów
IntStream.of(4, 5, 7, 42) // strumienie intów
DoubleStream.of(2.5, 4.5) // strumienie doubli

Mamy tu pewien rodzaj duplikacji, wymuszony „specjalnym” traktowaniem typów wbudowanych. Niestety, taka jest rzeczywistość tego języka progamowania i jedyny sposób żeby się pozbyć tej duplikacji wymagałby wprowadzenia ulepszonych generyków. Zauważ że jest 8 value types (boolean, int, long, char, byte, float, double, short), ale strumieni operujących na wartościach jest 4. Podejrzewam że pisanie tych wszystkich klas było dla nich wystarczająco irytujące, a zyski na tyle niewielkie (możesz użyć IntStream dla char i short, DoubleStream dla float), że po prostu sobie darowali.

Po utworzeniu takiego strumienia można na nim wywoływać operacje, które mogą, ale nie muszą zamknąć strumień. Po zamknięciu strumienia strumienia nie można go ponownie otworzyć lub przewinąć na początek – tak jak iteratory które nie są ForwardIteratorami w C++ lub iteratory w Pythonie.

Dzięki temu że operacje nie-terminujące zwracają strumień, można łączyć wywołania w łańcuch, o tak:

List<Integer> l = kolekcja.stream()
                          .filter((x) -> x < 5)
                          .map((x) -> x + 5)
                          .collect(Collectors.toList());

Tutaj mamy przykładową operację na strumieniach, których schemat można przedstawić tak:

  1. Tworzymy strumień
  2. Wołamy kolejne operacje
  3. Zbieramy wyniki

Spora część operacji jest leniwa, dzięki czemu można operować na strumieniach nieskończonych – zupełnie na listach w Haskellu. Nie wszystkie operacje są leniwe – w razie wątpliwości polecam skonsultować się z dokumentacją.

Podstawowe operatory funkcjonalne są – mamy map, reduce i filter. W przypadku map trzeba zwracać uwagę na typ wartości zwracanej, na co się nadziałem na sam start – jeżeli mamy strumień obiektów i to map() musimy użyć z funkcją Object -> Object. Jak mamy funkcje co przyjmuje prymityw i zwrca Object albo na odwrót, to musimy użyć odpowiednich operacji dla tych funkcji.

stream.map(/* funkcja Object -> Object */) // zwraca Stream
stream.mapToInt(/* funkcja Object -> int */) // zwraca IntStream
stream.mapToDouble(/* funkcja Object -> double */) // zwraca DoubleStream
stream.mapToLong(/* funkcja Object -> long */) // zwraca LongStream
int_stream.map(/* funkcja int -> int */) // zwraca IntStream
int_stream.mapToObj(/* funkcja int -> Object */) // zwraca Stream

Nie ma tu za krzty generyczności, o czym świadczy fakt, że te wszystkie klasy ktoś musiał napisać te wszystkie klasy, metody i interfejsy (każda funkcja map* przyjmuje osobny interfejs funkcjonalny). Co gorsza, nie ma metod Stream.mapToObj ani IntStream.mapToInt, więc musisz się też martwić jaki jest strumień po lewej stronie i użyć odpowiedniej nazwy.

Na koniec zbieramy wyniki: na przykład poprzez sum, collect, reduce, count. first. Z tych dwóch na uwagę zasługują collect oraz reduce:

  • reduce jest w dwóch postaciach: dwuargumentowej (T, T -> T -> T) zwracającej T będący redukcją, lub jeżeli jest pusty, lewy argument; oraz jednoargumentowej zwracającej Optional<T>, ponieważ null jest bezużyteczny – nie można na nim wołać metod ani robić cokolwiek sensownego poza porównaniem z innym obiektem.
  • collect to ogólne pojęcie na „zbierz te wszystkie wartości i zwróć mi je w tej postaci”. Mamy metodę collect która przyjmuje trzy operacje: utwórz wartość początkową, dodaj coś, złącz wartości, które dla np. Collections API odpowiadają funkcjom List::new, List::add i List::addAll. Ponieważ te trzy operacje są ściśle połączone, interfejs Collector łączy je w jedną całość jako jeden kolektor który można przekazywać. Gotowe kolektory można znaleźć w java.util.Stream.Collectors:

    • Collectors.toList() – zwraca listę ktora zawiera elementy ze strumienia (nie ma żadnych gwarancji której klasy implementującej List<T> obiekt zostanie zwrócony)
    • Collectors.toCollection() – zwraca kolekcje konkretnej klasy utworzonej przez funkcje podaną jako argument (np. ArrayList<T>::new)

Podsumowując, Java 8 Stream API jest bardzo uniwersalne i będę je używał wszędzie gdzie się da 😛

3 myśli nt. „Wrażenia z używania Stream API w Javie 8

  1. Drivaaqi83c

    http://pregowski.pl/sprzet-motoryzacyjny/opony-oryginalnych-marek
    Looking for a used or new automobile can be quite a tough procedure unless you know what you are actually performing. By educating yourself about car purchasing prior to deciding to go to the dealer, you can make stuff much easier for yourself. The following tips will help your following purchasing journey become more satisfying.

    Constantly provide a auto mechanic alongside when searching for a fresh vehicle. Car sellers are popular for promoting lemons and you may not wish to be their up coming target. Whenever you can not get a auto mechanic to consider autos along, no less than ensure that you have him take a look at closing choice before buying it.

    Know your limitations. Before you start store shopping for your next automobile or truck, determine how much you can afford to spend, and follow it. Don’t forget about to include fascination with your computations. You will definitely shell out around 20 percent as a down payment as well, so be prepared.

    Prior to going to a dealership, know what sort of car you need. Study most of you possibilities ahead of buying in order to decide what works best for your finances and loved ones demands. Seek information to determine exactly how much you must pay for a probable car.

    Before you sign any deal take the time to read each series, such as the small print. If you have anything at all detailed you do not understand, will not indication before you purchase an respond to which you fully grasp. Unsavory salesmen are able to use a legal contract to put in a lot of charges that were not discussed.

    When you keep the preceding suggestions in mind next time which you go purchasing a car, you will end up more prone to get a full deal. Investing in a car lacks as a head ache. Simply use the tips from this post and you can get the automobile you desire in a great cost.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax