Java'nın Veri Dünyasına Lambda İle Yolculuğu - III
Lambda İfadelerinin Tam Zamanı
Geldiğimiz noktada yaşadığımız sorunların tümünü lambda ifadelerini kullanarak çözebiliriz. Fakat öncesinde bir miktar sadeleştirme yapmamız gerekecek.
java.util.function
Bir önceki örnekte fonksiyonel arayüz olan MyTest arayüzünü metotlara parametre geçirirken kullanmıştık. Aslında böyle bir arayüzü yazmamıza gerek yok, zira Java SE 8 java.util.function paketi ile bize bu amaçlar için kullanabileceğimiz çok sayıda hazır arayüz sunulmaktadır. Bizim durumumuz için Predicate arayüzü tam olarak işimizi görmektedir.
3 public interface Predicate<T> {
4 public boolean test(T t);
5 }
Buradaki test metodu değişken olarak generic bir sınıf kabul etmekte ve boolean bir değer dönmektedir ki bu da bizim tam olarak ihtiyacımız olan şeydir. Bu arayüzü de kodumuza eklediğimizde son hali şu şekilde olacaktır.
RoboContactsLambda.java
1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Predicate;
5
6 /**
7 *
8 * @author MikeW
9 */
10 public class RoboContactLambda {
11 public void phoneContacts(List<Person> pl, Predicate<Person> pred){
12 for(Person p:pl){
13 if (pred.test(p)){
14 roboCall(p);
15 }
16 }
17 }
18
19 public void emailContacts(List<Person> pl, Predicate<Person> pred){
20 for(Person p:pl){
21 if (pred.test(p)){
22 roboEmail(p);
23 }
24 }
25 }
26
27 public void mailContacts(List<Person> pl, Predicate<Person> pred){
28 for(Person p:pl){
29 if (pred.test(p)){
30 roboMail(p);
31 }
32 }
33 }
34
35 public void roboCall(Person p){
36 System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
37 }
38
39 public void roboEmail(Person p){
40 System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
41 }
42
43 public void roboMail(Person p){
44 System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
45 }
46
47 }
Son değişiklik ile kullandığımız fonksiyonel arayüzü, tanımlı bir arayüz olan Predicate ile değiştirmiş olduk. Bu yaklaşımla sadece üç metot kullanarak seçim yapma imkânımız olacak. Metotlara geçireceğimiz lambda ifadeleri sayesinde şartları sağlayan Person nesnelerinin yığınlarını elde etmiş olacağız.
Dikey Problem Çözüldü
Lambda ifadeleri sayesinde dikey problemimiz çözüldü ve böylece ifadelerin tekrar kullanımı artık mümkün hale geldi. Şimdi kodumuzun lambda ifadeleri eklenerek düzenlenmiş son haline bir bakalım.
RoboCallTest04.java
1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Predicate;
5
6 /**
7 *
8 * @author MikeW
9 */
10 public class RoboCallTest04 {
11
12 public static void main(String[] args){
13
14 List<Person> pl = Person.createShortList();
15 RoboContactLambda robo = new RoboContactLambda();
16
17 // Predicates
18 Predicate<Person> allDrivers = p -> p.getAge() >= 16;
19 Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 &&
p.getGender() == Gender.MALE;
20 Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65;
21
22 System.out.println("\n==== Test 04 ====");
23 System.out.println("\n=== Calling all Drivers ===");
24 robo.phoneContacts(pl, allDrivers);
25
26 System.out.println("\n=== Emailing all Draftees ===");
27 robo.emailContacts(pl, allDraftees);
28
29 System.out.println("\n=== Mail all Pilots ===");
30 robo.mailContacts(pl, allPilots);
31
32 // Mix and match becomes easy
33 System.out.println("\n=== Mail all Draftees ===");
34 robo.mailContacts(pl, allDraftees);
35
36 System.out.println("\n=== Call all Pilots ===");
37 robo.phoneContacts(pl, allPilots);
38
39 }
40 }
Gördüğünüz gibi her bir grup seçimi için birer Predicate arayüzü tanımlandı. (allDrivers, allDraftees ve allPilots) Bu arayüz tanımlarından herhangi birini grupların ilgili işlemlerini yapan metotlara parametre olarak geçirebiliriz. Böylece kodumuz daha derli toplu, kolay okunabilir ve tekrarları olmayan bir hale dönüşmüş oldu.
3 public interface Predicate<T> {
4 public boolean test(T t);
5 }
|
1 package com.example.lambda;
2 3 import java.util.List; 4 import java.util.function.Predicate; 5 6 /** 7 * 8 * @author MikeW 9 */ 10 public class RoboContactLambda { 11 public void phoneContacts(List<Person> pl, Predicate<Person> pred){ 12 for(Person p:pl){ 13 if (pred.test(p)){ 14 roboCall(p); 15 } 16 } 17 } 18 19 public void emailContacts(List<Person> pl, Predicate<Person> pred){ 20 for(Person p:pl){ 21 if (pred.test(p)){ 22 roboEmail(p); 23 } 24 } 25 } 26 27 public void mailContacts(List<Person> pl, Predicate<Person> pred){ 28 for(Person p:pl){ 29 if (pred.test(p)){ 30 roboMail(p); 31 } 32 } 33 } 34 35 public void roboCall(Person p){ 36 System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone()); 37 } 38 39 public void roboEmail(Person p){ 40 System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail()); 41 } 42 43 public void roboMail(Person p){ 44 System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress()); 45 } 46 47 } |
1 package com.example.lambda;
2 3 import java.util.List; 4 import java.util.function.Predicate; 5 6 /** 7 * 8 * @author MikeW 9 */ 10 public class RoboCallTest04 { 11 12 public static void main(String[] args){ 13 14 List<Person> pl = Person.createShortList(); 15 RoboContactLambda robo = new RoboContactLambda(); 16 17 // Predicates 18 Predicate<Person> allDrivers = p -> p.getAge() >= 16; 19 Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 &&
p.getGender() == Gender.MALE;
20 Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65; 21 22 System.out.println("\n==== Test 04 ===="); 23 System.out.println("\n=== Calling all Drivers ==="); 24 robo.phoneContacts(pl, allDrivers); 25 26 System.out.println("\n=== Emailing all Draftees ==="); 27 robo.emailContacts(pl, allDraftees); 28 29 System.out.println("\n=== Mail all Pilots ==="); 30 robo.mailContacts(pl, allPilots); 31 32 // Mix and match becomes easy 33 System.out.println("\n=== Mail all Draftees ==="); 34 robo.mailContacts(pl, allDraftees); 35 36 System.out.println("\n=== Call all Pilots ==="); 37 robo.phoneContacts(pl, allPilots); 38 39 } 40 } |
Bu pakette neler var :java.util.function
Yukarıda incelediğimiz Predicate fonksiyonel arayüzü gibi daha bir çok yeni arayüz olduğundan bahsetmiştik. Şimdi bunlardan başlıca bir kaç tanesini inceleyelim.
-
Predicate: değişken olarak, konu olan nesnenin kendisi parametre olarak geçiliyor ve boolean bir değer dönüyor.
-
Consumer: değişken olarak verilen nesne üzerinde bir işlem yapılıyor ve geri dönüş değeri yok.
-
Function: değişken olarak verilen bir nesne üzerinde işlem yapıp yeni bir nesne geri dönüyor.
-
Supplier: herhangi bir değişken almaksızın bir nesne geri dönüyor. Bir nevi nesne üreteci.
-
UnaryOperator: verilen değeri dönen tekil bir işlemci.
-
BinaryOperator: değişken olarak verilen iki nesneden karşılaştırıcısına göre küçük olanı döner.
Doğu tipi isimlendirme ve metot referansları
Batıdaki ülkelerde genellikle isimler yazılırken ilk olarak ad ardından soyad yazılır. Fakat doğu ülkelerinde ise genellikle soyad önce ad sonra yazılır. Şimdi, önceki örneği tekrar ele alıp Person nesnesini yazmak için daha esnek bir yapı kuralım. Bu yeni yapıda yazım işlemini hem doğu hem de batı tarzında yapabilmemiz gerekiyor.
Predicate: değişken olarak, konu olan nesnenin kendisi parametre olarak geçiliyor ve boolean bir değer dönüyor.
Consumer: değişken olarak verilen nesne üzerinde bir işlem yapılıyor ve geri dönüş değeri yok.
Function: değişken olarak verilen bir nesne üzerinde işlem yapıp yeni bir nesne geri dönüyor.
Supplier: herhangi bir değişken almaksızın bir nesne geri dönüyor. Bir nevi nesne üreteci.
UnaryOperator: verilen değeri dönen tekil bir işlemci.
BinaryOperator: değişken olarak verilen iki nesneden karşılaştırıcısına göre küçük olanı döner.
Lambda Öncesi Örneği
Aşağıda lambda kullanmadan nasıl çözüm üretildiğinin örneğini bulabilirsiniz.
Person.java
128 public void printWesternName(){
129
130 System.out.println("\nName: " + this.getGivenName() + " " + this.getSurName() + "\n" +
131 "Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" +
132 "EMail: " + this.getEmail() + "\n" +
133 "Phone: " + this.getPhone() + "\n" +
134 "Address: " + this.getAddress());
135 }
136
137
138
139 public void printEasternName(){
140
141 System.out.println("\nName: " + this.getSurName() + " " + this.getGivenName() + "\n" +
142 "Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" +
143 "EMail: " + this.getEmail() + "\n" +
144 "Phone: " + this.getPhone() + "\n" +
145 "Address: " + this.getAddress());
146 }
Her bir yazım tarzı için bir metot yazılarak sorun çözülüyor.
Fonksiyon Arayüzü
Function arayüzü bu sorun için iyi bir çözüm. Tek bir metodu var, apply ve görünümü ise şöyle :
public R apply(T t){ }
Genel bir sınıfı T değişken olarak alıp R yi geri dönüyor. Bizim örneğimizi için Person nesnesini alıp String nesnesini dönüyor. Şimdi bu bilgileri kullanarak hedeflediğimiz daha esnek metodu yazalım.
Person.java
123 public String printCustom(Function <Person, String> f){
124 return f.apply(this);
125 }
126
Daha basit bir görünüm var ama nasıl çalışıyor ? Aşağıdaki kod, uygulamanın nasıl yapıldığını gösteriyor.
128 public void printWesternName(){
129 130 System.out.println("\nName: " + this.getGivenName() + " " + this.getSurName() + "\n" + 131 "Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" + 132 "EMail: " + this.getEmail() + "\n" + 133 "Phone: " + this.getPhone() + "\n" + 134 "Address: " + this.getAddress()); 135 } 136 137 138 139 public void printEasternName(){ 140 141 System.out.println("\nName: " + this.getSurName() + " " + this.getGivenName() + "\n" + 142 "Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" + 143 "EMail: " + this.getEmail() + "\n" + 144 "Phone: " + this.getPhone() + "\n" + 145 "Address: " + this.getAddress()); 146 } |
123 public String printCustom(Function <Person, String> f){
124 return f.apply(this); 125 } 126 |
NameTestNew.java
9 public class NameTestNew {
10
11 public static void main(String[] args) {
12
13 System.out.println("\n==== NameTestNew02 ===");
14
15 List<Person> list1 = Person.createShortList();
16
17 // Print Custom First Name and e-mail
18 System.out.println("===Custom List===");
19 for (Person person:list1){
20 System.out.println(
21 person.printCustom(p -> "Name: " + p.getGivenName() + " EMail: " + p.getEmail())
22 );
23 }
24
25
26 // Define Western and Eastern Lambdas
27
28 Function<Person, String> westernStyle = p -> {
29 return "\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
30 "Age: " + p.getAge() + " " + "Gender: " + p.getGender() + "\n" +
31 "EMail: " + p.getEmail() + "\n" +
32 "Phone: " + p.getPhone() + "\n" +
33 "Address: " + p.getAddress();
34 };
35
36 Function<Person, String> easternStyle = p -> "\nName: " + p.getSurName() + " "
37 + p.getGivenName() + "\n" + "Age: " + p.getAge() + " " +
38 "Gender: " + p.getGender() + "\n" +
39 "EMail: " + p.getEmail() + "\n" +
40 "Phone: " + p.getPhone() + "\n" +
41 "Address: " + p.getAddress();
42
43 // Print Western List
44 System.out.println("\n===Western List===");
45 for (Person person:list1){
46 System.out.println(
47 person.printCustom(westernStyle)
48 );
49 }
50
51 // Print Eastern List
52 System.out.println("\n===Eastern List===");
53 for (Person person:list1){
54 System.out.println(
55 person.printCustom(easternStyle)
56 );
57 }
58
59
60 }
61 }
Kodu üç ayrı kısımda inceleyelim. İlk bölüm döngü olan bölüm. Buradaki işlem çok basit. Döngü içinde tüm Person nesneleri alınarak isim ve eposta alanları istenilen biçimde yazılıyor. İkinci bölümde iki fonksiyon tanımlanıyor. Bu tanım lambda ifadeleri ile yapılıyor. Burada satır değil ama gövde tipi bir lambda ifadesi tanımı yapılıyor. Verilen nesne için yapılacak işlemler bir blok halinde veriliyor. Sonuçta iki fonksiyon elde ediyoruz, batı ve doğu tarzı. Son bölümde ise iki adet döngü var. İlk döngüye batı tarzı, ikinciye doğu tarzı yazım biçim, değişken olarak geçiliyor..
Sonuç olarak sadece değişken olarak tanımlı fonksiyon verilerek tek bir metot ile sorun çözülmüş oldu. Böylece daha esnek bir yapıya sahip olduk. Yeni bir yazım türü eklemek istersek sadece fonksiyon tanımı yapıp bunu parametre olarak geçmemiz yeterli olacak. Eğer bu fonksiyonları bir Map içine koyarsak tekrar kullanım imkanını da bir hayli arttırmış oluruz.
Örnek Çıktılar
==== NameTestNew02 ===
===Custom List===
Name: Bob EMail: bob.baker@example.com
Name: Jane EMail: jane.doe@example.com
Name: John EMail: john.doe@example.com
Name: James EMail: james.johnson@example.com
Name: Joe EMail: joebob.bailey@example.com
Name: Phil EMail: phil.smith@examp;e.com
Name: Betty EMail: betty.jones@example.com
===Western List===
Name: Bob Baker
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Jane Doe
Age: 25 Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
===Eastern List===
Name: Baker Bob
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Doe Jane
Age: 25 Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
9 public class NameTestNew {
10 11 public static void main(String[] args) { 12 13 System.out.println("\n==== NameTestNew02 ==="); 14 15 List<Person> list1 = Person.createShortList(); 16 17 // Print Custom First Name and e-mail 18 System.out.println("===Custom List==="); 19 for (Person person:list1){ 20 System.out.println( 21 person.printCustom(p -> "Name: " + p.getGivenName() + " EMail: " + p.getEmail()) 22 ); 23 } 24 25 26 // Define Western and Eastern Lambdas 27 28 Function<Person, String> westernStyle = p -> { 29 return "\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" + 30 "Age: " + p.getAge() + " " + "Gender: " + p.getGender() + "\n" + 31 "EMail: " + p.getEmail() + "\n" + 32 "Phone: " + p.getPhone() + "\n" + 33 "Address: " + p.getAddress(); 34 }; 35 36 Function<Person, String> easternStyle = p -> "\nName: " + p.getSurName() + " " 37 + p.getGivenName() + "\n" + "Age: " + p.getAge() + " " + 38 "Gender: " + p.getGender() + "\n" + 39 "EMail: " + p.getEmail() + "\n" + 40 "Phone: " + p.getPhone() + "\n" + 41 "Address: " + p.getAddress(); 42 43 // Print Western List 44 System.out.println("\n===Western List==="); 45 for (Person person:list1){ 46 System.out.println( 47 person.printCustom(westernStyle) 48 ); 49 } 50 51 // Print Eastern List 52 System.out.println("\n===Eastern List==="); 53 for (Person person:list1){ 54 System.out.println( 55 person.printCustom(easternStyle) 56 ); 57 } 58 59 60 } 61 } |
==== NameTestNew02 ===
===Custom List=== Name: Bob EMail: bob.baker@example.com Name: Jane EMail: jane.doe@example.com Name: John EMail: john.doe@example.com Name: James EMail: james.johnson@example.com Name: Joe EMail: joebob.bailey@example.com Name: Phil EMail: phil.smith@examp;e.com Name: Betty EMail: betty.jones@example.com ===Western List=== Name: Bob Baker Age: 21 Gender: MALE EMail: bob.baker@example.com Phone: 201-121-4678 Address: 44 4th St, Smallville, KS 12333 Name: Jane Doe Age: 25 Gender: FEMALE EMail: jane.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333 ===Eastern List=== Name: Baker Bob Age: 21 Gender: MALE EMail: bob.baker@example.com Phone: 201-121-4678 Address: 44 4th St, Smallville, KS 12333 Name: Doe Jane Age: 25 Gender: FEMALE EMail: jane.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333 |
Lambda İfadeleri ve Yığınlarda Kullanımı
Önceki örnekte Function arayüzü inceleyip basit lambda ifadeleri ile nasıl çözüm üretildiğini incelemiştik. Bu bölümde ise yığın sınıflarını (Collection) lambda ifadeleri ile kullanarak nasıl daha da zengin bir kullanıma kavuştuğumuzu göreceğiz.
Şimdiye kadarki örneklerimizde yığın kullanımı çok fazla olmadı ancak lambdalar sayesinde artık yığın kullanımı farklı bir boyut kazandı. Şimdi bunlara bir göz atalım.
Sınıfları Toplama
Daha önce yaptığımız ayrıştırmayı hatırlarsak, şimdi bu grupları SearchCriteria sınıfı içinde bir yığında toplayarak genelden soyutlayalım.
SearchCriteria.java
1 package com.example.lambda;
2
3 import java.util.HashMap;
4 import java.util.Map;
5 import java.util.function.Predicate;
6
7 /**
8 *
9 * @author MikeW
10 */
11 public class SearchCriteria {
12
13 private final Map<String, Predicate<Person>> searchMap = new HashMap<>();
14
15 private SearchCriteria() {
16 super();
17 initSearchMap();
18 }
19
20 private void initSearchMap() {
21 Predicate<Person> allDrivers = p -> p.getAge() >= 16;
22 Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
23 Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65;
24
25 searchMap.put("allDrivers", allDrivers);
26 searchMap.put("allDraftees", allDraftees);
27 searchMap.put("allPilots", allPilots);
28
29 }
30
31 public Predicate<Person> getCriteria(String PredicateName) {
32 Predicate<Person> target;
33
34 target = searchMap.get(PredicateName);
35
36 if (target == null) {
37
38 System.out.println("Search Criteria not found... ");
39 System.exit(1);
40
41 }
42
43 return target;
44
45 }
46
47 public static SearchCriteria getInstance() {
48 return new SearchCriteria();
49 }
50 }
Döngüler
Şimdi yeni bir özellik olarak yığın sınıflarına eklenen forEach metodunu inceleyelim. Aşağıdaki kod parçasında Person nesne listesi üzerindeki döngü ile yazım yapan bir kaç örneği bulabilirsiniz.
1 package com.example.lambda;
2 3 import java.util.HashMap; 4 import java.util.Map; 5 import java.util.function.Predicate; 6 7 /** 8 * 9 * @author MikeW 10 */ 11 public class SearchCriteria { 12 13 private final Map<String, Predicate<Person>> searchMap = new HashMap<>(); 14 15 private SearchCriteria() { 16 super(); 17 initSearchMap(); 18 } 19 20 private void initSearchMap() { 21 Predicate<Person> allDrivers = p -> p.getAge() >= 16; 22 Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE; 23 Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65; 24 25 searchMap.put("allDrivers", allDrivers); 26 searchMap.put("allDraftees", allDraftees); 27 searchMap.put("allPilots", allPilots); 28 29 } 30 31 public Predicate<Person> getCriteria(String PredicateName) { 32 Predicate<Person> target; 33 34 target = searchMap.get(PredicateName); 35 36 if (target == null) { 37 38 System.out.println("Search Criteria not found... "); 39 System.exit(1); 40 41 } 42 43 return target; 44 45 } 46 47 public static SearchCriteria getInstance() { 48 return new SearchCriteria(); 49 } 50 } |
Test01ForEach.java
11 public class Test01ForEach {
12
13 public static void main(String[] args) {
14
15 List<Person> pl = Person.createShortList();
16
17 System.out.println("\n=== Western Phone List ===");
18 pl.forEach( p -> p.printWesternName() );
19
20 System.out.println("\n=== Eastern Phone List ===");
21 pl.forEach(Person::printEasternName);
22
23 System.out.println("\n=== Custom Phone List ===");
24 pl.forEach(p -> { System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail())); });
25
26 }
27
28 }
İlk örnekte lambda ifadesi verilerek printWesternName metodu çağrılıyor ve her bir nesne için bu yazım işlemi yapılıyor. İkinci örnek ise metot referansı kullanarak işlem yapmaktadır. Bu da yeni bir kullanım türüdür. Eğer nesnede tanımlı bir metot var ise normal lambda ifadesi yerine bu tür bir kullanım da mümkündür. Son olarak ise printCustom metodu nasıl kullanılabilir onu görüyoruz. Burada biraz kafa karışıklığı olabilir zira lambda içinde lambda kulanımı var ve değişken isimlerinin iç içe kullanım olduğunda nasıl değiştiğine dikkat edilmeli.
Bu şekilde herhangi bir yığın içinde tekrarlı işlem yapmak mümkündür. Aslında burada yapılan işlem for döngüsüne çok benzemekle birlikte işlemin sınıfın içinde dışarıya kapalı olarak yapılması bir çok faydayı beraberinde getirmektedir.
Zincirleme Filtre Kullanımı
Dizinlerin içeriği üzerinde döngü ile işlem yapma imkanına ek olarak yığın metotlarını zincirleme olarak da kullanmak artık mümkün. İlk olarak Predicate arayüzünü değişken olarak alan filter metoduna bir bakalım.
Aşağıdaki örnekte, filtreleme adımı sonrası List nesnesi üzerinde döngü ile işlem yapılıyor.
11 public class Test01ForEach {
12 13 public static void main(String[] args) { 14 15 List<Person> pl = Person.createShortList(); 16 17 System.out.println("\n=== Western Phone List ==="); 18 pl.forEach( p -> p.printWesternName() ); 19 20 System.out.println("\n=== Eastern Phone List ==="); 21 pl.forEach(Person::printEasternName); 22 23 System.out.println("\n=== Custom Phone List ==="); 24 pl.forEach(p -> { System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail())); }); 25 26 } 27 28 } |
Test02Filter.java
9 public class Test02Filter {
10
11 public static void main(String[] args) {
12
13 List<Person> pl = Person.createShortList();
14
15 SearchCriteria search = SearchCriteria.getInstance();
16
17 System.out.println("\n=== Western Pilot Phone List ===");
18
19 pl.stream().filter(search.getCriteria("allPilots"))
20 .forEach(Person::printWesternName);
21
22
23 System.out.println("\n=== Eastern Draftee Phone List ===");
24
25 pl.stream().filter(search.getCriteria("allDraftees"))
26 .forEach(Person::printEasternName);
27
28 }
29 }
İlk ve son döngüde arama kısıtına bağlı nasıl filtreleme yapıldığı görülüyor. Çıktı ise şu şekilde :
=== Eastern Draftee Phone List ===
Name: Baker Bob
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Doe John
Age: 25 Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Biraz Tembelleşelim
Gördüğümüz gibi bu özellikler gerçekten etkileyici. İyi ama eğer bizim nesneler içinde döngü yapma imkanımız varsa neden bir yığına doldurup orada çalışalım ki ? Eğer nesne içinde tarama imkânını kütüphanelere eklersek Java geliştiricilerinin daha verimli kod yazmasına imkân sunmuş oluruz. Bu ne demek diyorsanız, ilk olarak şu iki kavramı bir tanımlayalım.
-
Tembellik (Laziness) : Programlama dünyasında tembellik demek, sadece ihtiyacın olan nesne ile ve sadece ihtiyacın olduğu zamanda işlem yapmak demek. Örneğin bir önceki örnekteki son döngü bu kapsama giriyor çünkü ilk olarak filtreleme yapıldığından döngü sadece kalan iki Person nesnesi için çalışıyor.
-
Açgözlülük (Eagerness) : Bir yığın içindeki tüm kayıtlar için işlem yapılıyorsa bu kapsama giriyor. Eğer sadece iki nesne ile işlem yapacakken bütün liste üzerinde döngü çalışıyorsa buna açgözlü yaklaşım denir.
Eğer döngü özelliği kütüphaneye eklenirse, fırsat çıktığında tembellik özelliği kullanarak daha verimli kod yazmak mümkün hale gelir. Tabii ki ortalama alma veya toplama işlemi gibi bir işlem yapıyorsanız tüm liste üzerinde çalışmak bir zorunluluktur ve açgözlü işlem seçimi bu bağlamda olması gerekendir. Ama zorunlu haller dışında kesinlikle tembellik yöntemi tercih edilmelidir.
Stream Metodu
Bir önceki örneğe bakarsak, filtreleme işlemi ve döngü ile filtrelenen değerler üzerinde işlem yapmadan önce stream metodu çağrılıyor. Bu metot Collection nesnesini değişken olarak alıp geriye java.util.stream.Stream nesnesi dönüyor. Burada Stream nesnesi, üzerinde birden çok faklı zincirleme metot çağırma imkânı veren bir öğeler dizisi anlamına gelir. Aksi belirtilmediği taktirde eğer bir öğe diziden alınırsa harcanmış olur ve tekrar kullanılamaz. Bu nedenle eğer zincirleme metot kullanımı söz konusu ise, her metot, her defasında bir önceki kullanımdan kalan ve farklı öğe grubu üzerinde çalışacaktır. Ayrıca istenirse Stream kullanılan metoda bağlı olarak paralel ya da seri olarak da işlenebilir. En sonda paralel işlemeye ait bir örnek olacak.
9 public class Test02Filter {
10 11 public static void main(String[] args) { 12 13 List<Person> pl = Person.createShortList(); 14 15 SearchCriteria search = SearchCriteria.getInstance(); 16 17 System.out.println("\n=== Western Pilot Phone List ==="); 18 19 pl.stream().filter(search.getCriteria("allPilots")) 20 .forEach(Person::printWesternName); 21 22 23 System.out.println("\n=== Eastern Draftee Phone List ==="); 24 25 pl.stream().filter(search.getCriteria("allDraftees")) 26 .forEach(Person::printEasternName); 27 28 } 29 } |
=== Eastern Draftee Phone List ===
Name: Baker Bob Age: 21 Gender: MALE EMail: bob.baker@example.com Phone: 201-121-4678 Address: 44 4th St, Smallville, KS 12333 Name: Doe John Age: 25 Gender: MALE EMail: john.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333 |
Tembellik (Laziness) : Programlama dünyasında tembellik demek, sadece ihtiyacın olan nesne ile ve sadece ihtiyacın olduğu zamanda işlem yapmak demek. Örneğin bir önceki örnekteki son döngü bu kapsama giriyor çünkü ilk olarak filtreleme yapıldığından döngü sadece kalan iki Person nesnesi için çalışıyor.
Açgözlülük (Eagerness) : Bir yığın içindeki tüm kayıtlar için işlem yapılıyorsa bu kapsama giriyor. Eğer sadece iki nesne ile işlem yapacakken bütün liste üzerinde döngü çalışıyorsa buna açgözlü yaklaşım denir.
Dönüşüm ve Sonuçları
Az öncede değindiğimiz gibi Stream harcanıp biten bir yapıya sahiptir. Bu nedenle de eğer Stream kullanıyorsak, bu yığındaki öğeleri değiştirme veya dönüştürme işlemine tabi tutamayız. Peki eğer böyle bir ihtiyacımız var ise, yani harcanmasın elimizde kalsın diyorsak ne yapacağız ? Buyrun bakalım, aşağıdaki örnek bu işi nasıl yapıyor.
Test03toList.java
10 public class Test03toList {
11
12 public static void main(String[] args) {
13
14 List<Person> pl = Person.createShortList();
15
16 SearchCriteria search = SearchCriteria.getInstance();
17
18 // Make a new list after filtering.
19 List<Person> pilotList = pl
20 .stream()
21 .filter(search.getCriteria("allPilots"))
22 .collect(Collectors.toList());
23
24 System.out.println("\n=== Western Pilot Phone List ===");
25 pilotList.forEach(Person::printWesternName);
26
27 }
28
29 }
Kodu incelediğimizde collect metodunun Collectors tipinde bir nesne geçirilerek çağrıldığını görüyoruz. Burada kullanılan nesne Stream’da bulunan veriye göre List ya da Set dönebiliyor. Bizim örneğimiz için dönüş değeri döngü ile üzerinde işlem yapılabilen bir List oluyor.
Map Kullanarak Hesaplama
Map metodu genellikle filter metodu ile birlikte kullanılır. Bu metot sınıfın öğelerinden birini alıp üzerinde bir işlem yapar. Aşağıda kişi yaşı üzerinde işlem yapan bir örnek kod var.
10 public class Test03toList {
11 12 public static void main(String[] args) { 13 14 List<Person> pl = Person.createShortList(); 15 16 SearchCriteria search = SearchCriteria.getInstance(); 17 18 // Make a new list after filtering. 19 List<Person> pilotList = pl 20 .stream() 21 .filter(search.getCriteria("allPilots")) 22 .collect(Collectors.toList()); 23 24 System.out.println("\n=== Western Pilot Phone List ==="); 25 pilotList.forEach(Person::printWesternName); 26 27 } 28 29 } |
Test04Map.java
10 public class Test04Map {
11
12 public static void main(String[] args) {
13 List<Person> pl = Person.createShortList();
14
15 SearchCriteria search = SearchCriteria.getInstance();
16
17 // Calc average age of pilots old style
18 System.out.println("== Calc Old Style ==");
19 int sum = 0;
20 int count = 0;
21
22 for (Person p:pl){
23 if (p.getAge() >= 23 && p.getAge() <= 65 ){
24 sum = sum + p.getAge();
25 count++;
26 }
27 }
28
29 long average = sum / count;
30 System.out.println("Total Ages: " + sum);
31 System.out.println("Average Age: " + average);
32
33
34 // Get sum of ages
35 System.out.println("\n== Calc New Style ==");
36 long totalAge = pl
37 .stream()
38 .filter(search.getCriteria("allPilots"))
39 .mapToInt(p -> p.getAge())
40 .sum();
41
42 // Get average of ages
43 OptionalDouble averageAge = pl
44 .parallelStream()
45 .filter(search.getCriteria("allPilots"))
46 .mapToDouble(p -> p.getAge())
47 .average();
48
49 System.out.println("Total Ages: " + totalAge);
50 System.out.println("Average Age: " + averageAge.getAsDouble());
51
52 }
53
54 }
Bu da çıktısı :
== Calc Old Style ==
Total Ages: 150
Average Age: 37
== Calc New Style ==
Total Ages: 150
Average Age: 37.5
Bu kod liste içinde buluna pilotların yaşlarının ortalamasını hesaplıyor. Birinci döngü eski usûl hesaplamayı for döngüsü yardımı ile örnekliyor. İkinci döngü ise seri stream üzerinden mapToInt metodu kullanarak her bir bireyin yaşını alıyor ve dönüş değeri IntStream tipinde bir nesne dönüyor ki bu nesnenin metotları arasında bulunan geri dönüş değeri long olan sum metodu sayesinde de yaşları toplayabiliyoruz. Ortalamayı bulmak için yaşları toplamaya gerek yok ama sum metodunun kullanımına örnek olması açısından değinildi.
Son döngü ise pilotların yaşlarının ortalamasını hesaplıyor. Burada dikkat edilmesi gereken hesaplamanın eş zamanlı olarak paralel yapılabilmesi amacı ile parallelStream metodunun kullanılmasıdır.
10 public class Test04Map {
11 12 public static void main(String[] args) { 13 List<Person> pl = Person.createShortList(); 14 15 SearchCriteria search = SearchCriteria.getInstance(); 16 17 // Calc average age of pilots old style 18 System.out.println("== Calc Old Style =="); 19 int sum = 0; 20 int count = 0; 21 22 for (Person p:pl){ 23 if (p.getAge() >= 23 && p.getAge() <= 65 ){ 24 sum = sum + p.getAge(); 25 count++; 26 } 27 } 28 29 long average = sum / count; 30 System.out.println("Total Ages: " + sum); 31 System.out.println("Average Age: " + average); 32 33 34 // Get sum of ages 35 System.out.println("\n== Calc New Style =="); 36 long totalAge = pl 37 .stream() 38 .filter(search.getCriteria("allPilots")) 39 .mapToInt(p -> p.getAge()) 40 .sum(); 41 42 // Get average of ages 43 OptionalDouble averageAge = pl 44 .parallelStream() 45 .filter(search.getCriteria("allPilots")) 46 .mapToDouble(p -> p.getAge()) 47 .average(); 48 49 System.out.println("Total Ages: " + totalAge); 50 System.out.println("Average Age: " + averageAge.getAsDouble()); 51 52 } 53 54 } |
== Calc Old Style ==
Total Ages: 150 Average Age: 37 == Calc New Style == Total Ages: 150 Average Age: 37.5 |
Comments
Post a Comment