Java 8 Stream yapısına nazik bir giriş - 1

Bu yazıda Java 8 de Collection Interface ile beraber gelen stream yapısını ele alıyoruz. Başta bu yapının Lambda ifadeleri ile sağladığı yazım kolaylıklarından bahsedip (buz dağının görünen yüzü), yazının sonuna doğru, stream tipini nasıl değiştirebileceğimizi göreceğiz.

Stream yapısı, Collection interface ini extend eden classlara gelen bir özellik. Örnek vermek gerekirse bu Class’lar ; her gün bir çok kez kullandığımız List, Set ve Queue ‘dan başkası değil.

Stream kullanımı bugüne kadar alışa geldiğimiz, Collection elementlerinin içinde dolaşma şeklimizi değiştiriyor.

Örnek olarak; elimizde bir personel listesi olsun ve bizden istenende listedeki bütün elemanlarının yaşının toplamını bulan bir fonksiyon yazmak.

Java 7 ve öncesinde uyguladığımız yöntem;

Burada kısaca yaptığımız, bize verilen personel listesinin elemanlarını for each ile dolaşarak, yaşlarının toplamını bulmak. Aynısını stream ile yapmaya çalışır isek;

Hiç yapmasak daha iyiydi gibi gözüküyor. Hem satır sayısı fazlalaştı hemde kod daha karışık gözüküyor. Bir de neden olduğu belirsiz final bir Integer Array’i var elimizde. Sorun değil hepsinin açıklaması var, ayrıca bölümün sonunda tek satıra indireceğimizin garantisini veriyorum.

İlk olarak kısaca yapılanı açıklıyorum; personel listesinin stream methodunu çağırarak, bundan sonra stream objesi ile iş yapacağımızı belirtiyoruz. En anlaşılması rahat olan forEach methodunu çağırıyoruz. ForEach Methodu parametre olarak Consumer<T> () interface’ini extend eden bir Class objesi alıyor. Consumer interface’i accept method’unda T generic T tipinden oluşan objeyi parametre olarak alır ve geri hiç bir değer döndürmez. Bu class’ı hemen Anonymous Class olarak oluştuyoruz.

Niçin Final Integer Array ?

Anonymous Class bildiğiniz üzere bir inner class ve eğer kendi ana Class’ınız içerisinde kullandığınız bir değişkeni, inner class içerisindede kullanmak isterseniz bunu final olarak tanımlamanız gerekiyor. Ancak bunu yaptığınızda i +=personnel.getAge() satırı haliyle hata veriyor, çünkü final olarak tanımladığınız bir alana tekrar atama(initialize) yapıyorsunuz. Bu hatayı geçmek için IDE’nin ipucu olarak verdiği mini hileyi uyguluyoruz ve final değişken olarak Integer arrayi oluşturup toplama işlemini array’in ilk elemanı üzerinden gerçekleştiriyoruz. Böylece Array’i tekrar initialize etmiyoruz ancak ilk elemanının değerini istediğimiz kadar değiştiririz.

Kod Sadeleştirme

Şimdi gelelim, bu hesaplamayı nasıl tek satırda yapabiliriz. Öncelikle kodu kısaltabileceğimiz ilk adım, Lambda ifadelerini kullanmak,

new Consumer() diye başladığımız yeri, sadece Accept Methodunu yazabilecek şekilde sadeleştirebiliriz. Kod’un yeni hali aşağıdaki gibi olacaktır.

Parametre olarak personnel geliyor ve herhangi bir şey dönmeden işlemimizi tek satırda gerçekleştiriyoruz.

O zaman devam edelim. Halen gözü çok rahatsız eden final integer arrayden kurtulamadık.

Stream yapısının bir diğer özelliği ise, streamlerin tipini herhangi bir T tipine map edebilirsiniz. Yani demek istenilen; şuan elimizde bir personel stream i var, ben bu personel streamini, personel’in yaşlarını alıp , sadece yaş içeren ( Integer ) bir stream’e çevirebilirim. Kodu görelim.

Burda kullandığımız Anonymous Class ise ToIntFunction adında T tipini alıp Integer dönen bir interface. Java 8 default olarak size Integer, Long ve Double a dönüştüren interface’leri vermiş ancak siz başka bir Y tipine dönüştürmek istiyor iseniz direk Function<T , Y> interface ini kullanabilirsiniz.

Tamam şuan elimizde sadece yaşları içeren bir int stream var, teoride iyiyiz, ancak pratikte görüldüğü gibi dönen SIFIR.

Yapmamız gereken, integer stream’indeki değerleri toplayıp geriye tek bir Integer değer dönmek. Bundan öncesinde lambda ifadesini kullanıp, şu kodu daha kısa hale getirelim.

Eğer parametre olarak gelen bir objenin fonksiyonunu kullanıp , geriye beklenilen değeri (Integer) dönüyor iseniz, method referansını direk bu şekilde kullanabilirsiniz.

OK elimizde hala integer stream’i var, son olarak bu stream’i tek bir integer dönecek şekilde sonlandırmamız. Integer streamlere ( IntStream.class ) bakacak olursanız, sum adında bir method göreceksiniz. Bu method stream içerisindeki bütün değerleri toplayıp size tek bir değer dönecektir. Bunun ile beraber, başta söz verdiğimiz gibi tek satırda bütün yaşların toplamını bulan işlemi gerçekleştirmiş olduk.

Serinin ilk yazısında sadece stream yapısını yüzeysel tanımış olduk. İkinci yazıda daha detaylı olarak, stream üzerinde kullanabileceğimiz fonksiyonları (filter vs.) inceleyeceğiz.

Bu sayfadaki kodları test edebileceğiniz örnek proje için, GitHub adresini kullanabilirsiniz.

https://github.com/sinanselimoglu/streamsamples