Java 8 Stream Tutorial część 3Java 8 Stream Tutorial part 3

No dobra, trochę prostych przykładów za nami, dowiedzieliśmy się, że za pomocą streamów, wyrażeń lambda można uprościć kod, można wykonać pewne elementy na wielu wątkach, ale co to takiego są te lambdy? Czy można część kodu w tych dziwnych lambdach wynieść do osobnych metod, i sprawić, aby były dostępne w innych miejscach? Jak to zrobić, aby się nie narobić ? 🙂

Ok, we have made some simple examples, we know that thanks to streams and lambda expressions we can make code simpler, we can use many threads, but… what are all those lambdas? Can we simplify them, move them to other methods so they can be reused ?

Na początek lambda. Lambda to nic innego jak znaczek  ” -> ” 😛

A tak na prawdę, to po prostu anonimowa funkcja, która implementuje metodę apply()

Czyli to po prostu jedna metoda, która coś wykonuje, czasem coś zwraca, czasem nie.

Mamy kilka podstawowych metod w operacjach na streamach:

– map – aby wynieść operację z map do osobnych metod musimy zaimplementować Function<Object,Object>

– filter – to jest tak na prawdę implementacja Predicate

– reduce –  to jest BinaryOperator

– foreach – to po prostu Consumer

 

Fajnie, ale wygląda na cholernie trudne. Jak się okazuje wcale nie 😉

Przykład jak zawsze na github -> https://github.com/najavie/streamExample2

Na początek stworzyłem klasę Person

Nic zaawansowanego. Proste POJO, getery, setery, jakiś toString

Pamiętacie generowanie Osób za pomocą IntStream? A może dałoby się jakoś napisać własny taki losowy generator Person? Pewnie, że się da, i okazuje się to banalne…

Napisałem prostą klasę, która implementuje Interfejs Supplier<Object o>. W diamondach podajemy jaki ma być typ wynikowy. W moim przypadku Person.

Interfejs Supplier wymaga implementacji jednej metody: get(), która musi mi zwrócić Person. No to sobie zrobiłem prostą implementację metody get()

Aby sprawdzić, czy działa, napisałem sobie też prosty test.

 

I to właściwie wszystko – teraz tylko jakoś magicznie trzeba podłączyć tego naszego PersonSupplier do Streama…

Metoda generate z klasy Stream wymaga funkcji Supplier… i dostała to co chciała i teraz robi to co my chcemy.

Mamy już stream Person, które na moje zawałowanie wygenerują mi ich … w… ile zechcę.

No to mam listę 100 Person. Extra… Co ja z tym mogę zrobić?

Napisałem generator tak wrednie, że nie mam dat urodzin w obiektach Person. Może dodam do kilku? Nie do wszystkich, byłoby za prosto…

Napiszmy więc jakąś metodę dla filtra, który mi wybierze trochę osób…

Tutaj właściwie jest tak samo jak w przypadku Supplier’a. Implementujemy Predicate, Predicate ma jedną metodę test zwracającą boolean. No to sobie napisałem coś prostego, co zwraca true albo false na podstawie hashcode przekazanego obiektu.

No to teraz implementacja…

i tak oto dla każdego elementu streama, zostanie uruchomiony filtr, który wykona metodę test, która zwróci true albo false. Jeśli true, element zostaje, jeśli false… idzie precz.

No dobra… a teraz do tych co zostały, chciałbym dodać daty urodzin… Możemy zastosować metodę foreach… a foreach jest to… Consumer.. więc napiszmy consumera…

Znowu – implementujemy Consumer, ma on jedną metodą Accept, która jako parametr przyjmuje to co podaliśmy w diamondach… i coś na tym obiekcie robimy…

Tutaj mała uwaga. To co zrobiłem, nie jest do końca poprawne, ponieważ wszystko to co jest w funkcji powinno być immutable, a ja zmieniam ten stan. Nie powinienem. Powinienem w takim przypadku stworzyć nowy obiekt. Ale na potrzeby takiego prostego przykładu, gdzie wiem, że nic się z tym obiektem dalej nie będzie złego działo mogłem sobie pozwolić na taki mały grzech…

I jeszcze test…

A teraz sama implementacja…

Razem z filtrem, zajęło to całą jedną linijkę. Dodatkowo mogę sobie te filtry, oraz consumera używać kiedy chcę. Pamiętajmy o DRY (Don’t Repeat Yourself)

Co dalej?

Zobaczmy jakie daty mam wygenerowane…

A chciałbym mieć listę dat…

Są wsród nich nulle… da się coś z tym zrobić?

Zadanie dla chętnych – a jak by to zrobić, żeby mieć listę dat od samego początku bez nulli? 🙂

Klasa Person ma jakąś listę numerków… Po co? Nie wiem, ale może dodajmy jakieś…

Najpierw jakiś Consumer…

Tutaj też – uwaga jak wyżej – nie powinienem tak robić…

I teraz dodajmy już te numerki…

A teraz… trudne pytanie… jaka jest suma numerków każdej osoby?

Trudne pytanie, prosta odpowiedź 🙂

A implementacja…

A jaka jest suma wszystkich numerków?

Przyda się kolejna funkcja …

Ona jest chyba najtrudniejsza i sprawia najwięcej problemów…

implementacja BinaryOperator, typ Integer.

Chodzi o to, że ma on na podstawie danej X oraz Y, wygenerować daną Z, która będzie tego samego typu co X i Y. W tym przypadku dostajemy dwie liczby i je dodaję. Proste.

A jaka jest suma wszystkich numerków? mając taką funkcję, to już jest bułka z masłem…
Rozwiązań jest kilka.. oto dwa jakie mi przyszły do głowy…

Metoda reduce to właśnie BinaryOperator… Chodzi o to, że możemy mieć zmienną „na boku” (czyli tzw accumulator), która przechowa wyniki z operacji na danych, będzie thread-safe. Fajne i daje ogromne możliwości…

 

Uff…

Po tych kilku przykładach myślę, że każdy będzie w stanie zacząć pisać chociaż trochę funkcyjnie. Jak widać nie jest to trudne, mniej pisania = mniej błędów, łatwiejsze testowanie.

Polecam zagłębienie się w ten temat, poczytanie i… do dzieła 😉

 

 

First… Lamda is nothing more than this: ” -> ” 😛

It is anonymous method which implements apply() method.

To put is simple – it is one method.

We have some basic methods to work on streams:

  • map – to move map function to separeate method, we must implement Function<Object,Object>
  • filter – it is simple Predicate
  • reduce – this is BinaryOperator
  • foreach – Consumer !

 

It looks to be very difficult… but it is not… take a look…

My github example – https://github.com/najavie/streamExample2

First.. person class..

Simple POJO. some getters, setters, toString…

Remember generating person using IntStream from my previous post? Maybe we could write our own random Person Generator? Of course we can and it is very simple…

I’ve made a simple class which implements Interfejs Supplier<Object o>.  In diamonds a put a result type. In my example – Person.

Interface Suppliers requires only one method – get(), which must return Person. And here we have it 🙂

To check it, i’ve made simple test:

And thats it 🙂
Now let magic happen –

Generate method requires function Supplier…

Now we have a person Stream! And we can generate it as many as we need…

I have a list of 100 persons… Awesome!

I’ve made generator without birthdate.. Maybe i will add some… Not to all persons.. it would be too easy…

Let us write e filter method which will choose some persons…

Same logic as in Suppliers… We implement Predicate. Predicate has one method – test, which returns boolean.

True if hashcode is even…

Now implementation..

And here, for every element of stream there will be filter applied, and it will return true or false… It result is true elements stays…

And now to all filtered elements i would like to add birthday… We can use foreach method… Foreach is a consumer…

Again.. we implement Consumer, it has one method – accept…

And now some test…

And implementation…

All this is now one liner. Predicate and Consumer is a function which i can use in any place i want, so DRY (Don’t Repeat Yourself) rule is kept 🙂

What’s next?

Lets take a look what dates i have generated…

I would like to have a list of dates…

Oups.. there are nulls… What to do with them?

Task for you: How to make a list of dates without nulls from the begining?

 

Person Class has some list of numbers. What for? I do not know, but let’s add some…

First.. a Consumer…

And now let’s add those numbers…

What is a sum of numbers for each person?

And implementation…

What is a sum of all numbers?

There is another function required…

I think this is most difficult…

Implementation BinaryOperator, Integer

Basing on some X and Y i want to generate Z, which will be the same type as X and Y. In this case we get two Integers, and we add them… Simple..

What is the sum of all numbers? When we have such function, this is easy…

There is a lot of solutions, i’ve come up with two…

Reduce method is BinaryOperator… We can have one variable as accumulator, which will store internally result of operations. Cool and very useful…

That’s all. After those simple examples i think you will be able to start writing some functional elements. As you can see it is not difficult. It’s easier to test, takes less lines, and code is reusable…