1. Lambda ifadələri və kolleksiyalar
Lambda ifadələri yaranmazdan əvvəl kolleksiyaların emalı necə görünürdü, xatırlayaq. Tutaq ki, bizdə sətirlərdən ibarət siyahı var və onları ekrana çıxarmaq istəyirik:
List<String> list = Arrays.asList("pişik", "it", "kirpi");
for (String s : list) {
System.out.println(s);
}
Hər şey sadədir, amma məsələn, siyahıdan bütün boş sətirləri silmək istəsək, şərt ilə dövr yazmalı oluruq, bəzən isə iteratordan istifadə etməliyik (əks halda ConcurrentModificationException olacaq). Və ya, tutalım, sətirin uzunluğuna görə çeşidləmə:
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
Belə sadə tapşırıqda belə — artıq 5 sətir kod və xeyli «səs-küylü» mötərizələr. Necə optimallaşdıraq? Cavabı artıq bilirsiniz: lambda ifadələri.
Kolleksiya metodlarında lambda ifadələrinin tətbiqi
Java 8-dən etibarən kolleksiya interfeysləri funksional interfeysləri qəbul edən yeni metodlar aldı — yəni ora lambda ifadələri ötürə bilərik. Onların ən məşhurları:
- forEach(Consumer<T> action)
- removeIf(Predicate<T> filter)
- sort(Comparator<T> c)
- replaceAll(UnaryOperator<T> operator)
Nümunə: forEach
Siyahının bütün elementlərini ekrana çıxarmaq (köhnə üsul):
for (String s : list) {
System.out.println(s);
}
İndi — lambda ilə:
list.forEach(s -> System.out.println(s));
Və ya daha qısa, əgər «Java gurusu» oynamaq istəyirsinizsə:
list.forEach(System.out::println); // method reference, bunu sonra izah edəcəyik
Nümunə: removeIf
Siyahıdan bütün boş sətirləri silmək:
List<String> animals = new ArrayList<>(Arrays.asList("pişik", "", "it", "kirpi", ""));
animals.removeIf(s -> s.isEmpty());
System.out.println(animals); // [pişik, it, kirpi]
Nümunə: sort
Siyahını sətirin uzunluğuna görə çeşidləmə:
List<String> animals = Arrays.asList("pişik", "it", "kirpi", "fil");
animals.sort((a, b) -> a.length() - b.length());
System.out.println(animals); // [it, fil, pişik, kirpi]
Nümunə: replaceAll
Bütün sətirləri böyük hərflərə çevirmək:
List<String> animals = new ArrayList<>(Arrays.asList("pişik", "it", "kirpi"));
animals.replaceAll(s -> s.toUpperCase());
System.out.println(animals); // [PİŞİK, İT, KİRPİ]
2. Stream API və lambda ifadələri
Java 8 ilə birlikdə funksional üslubda kolleksiyaların emalı üçün güclü alət olan Stream API yarandı. Axınlar filtrasiya, çevirmə, çeşidləmə, metod zəncirləri vasitəsilə kolleksiyaların toplanmasını təmin edir. Və bu metodların hamısı lambda ifadələrini qəbul edir!
Vacibdir: Stream API-nin tam şərhini sonra edəcəyik, indi isə — yalnız lambda-ların rolunu anlamaq üçün əsas nümunələr.
Nümunə: filtrasiya
Yalnız uzunluğu 3 simvoldan böyük olan sətirləri saxlamaq:
List<String> animals = Arrays.asList("pişik", "fil", "kirpi", "krokodil");
animals.stream()
.filter(s -> s.length() > 3)
.forEach(System.out::println); // pişik, kirpi, krokodil
Nümunə: çevirmə (map)
Bütün sətirləri böyük hərflərə çevirmək:
List<String> animals = Arrays.asList("pişik", "fil", "kirpi");
List<String> upper = animals.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList());
System.out.println(upper); // [PİŞİK, FİL, KİRPİ]
Nümunə: çeşidləmə
Uzunluğa görə çeşidlənmiş siyahını əldə etmək (mənbəni dəyişmədən):
List<String> animals = Arrays.asList("pişik", "fil", "kirpi", "krokodil");
List<String> sorted = animals.stream()
.sorted((a, b) -> a.length() - b.length())
.collect(Collectors.toList());
System.out.println(sorted); // [fil, pişik, kirpi, krokodil]
3. Anonim siniflərlə müqayisə
Gəlin eyni kodun anonim siniflə və lambda ilə necə göründüyünü müqayisə edək.
Sətirin uzunluğuna görə çeşidləmə
Anonim sinif:
animals.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
Lambda ifadəsi:
animals.sort((a, b) -> a.length() - b.length());
Nəticə:
Lambda ifadəsi çox sətirə qənaət edir və kodu daha oxunaqlı edir. Daha az mötərizə, daha az səs-küy — daha çox məzmun!
Boş sətirlərin silinməsi
Anonim sinif:
animals.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.isEmpty();
}
});
Lambda ifadəsi:
animals.removeIf(s -> s.isEmpty());
4. Praktika: lambda ifadələri ilə qısa tapşırıqlar
Gəlin istifadəçilər siyahısı ilə işləyən kiçik bir proqramda lambda ifadələrini praktikada tətbiq edək.
Nümunə 1: İstifadəçilərin yaşa görə filtrasiya edilməsi
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
List<User> users = Arrays.asList(
new User("Alisa", 17),
new User("Bob", 25),
new User("Charlie", 15)
);
users.stream()
.filter(u -> u.age >= 18)
.forEach(System.out::println); // Bob (25)
Nümunə 2: İstifadəçilərin ada görə çeşidlənməsi
List<User> users = Arrays.asList(
new User("Alisa", 17),
new User("Bob", 25),
new User("Charlie", 15)
);
users.sort((u1, u2) -> u1.name.compareTo(u2.name));
System.out.println(users);
// [Alisa (17), Bob (25), Charlie (15)]
Nümunə 3: İstifadəçilər siyahısını adlar siyahısına çevirmək
List<String> names = users.stream()
.map(u -> u.name)
.collect(Collectors.toList());
System.out.println(names); // [Alisa, Bob, Charlie]
Nümunə 4: Bütün yetkinlik yaşına çatmayanları silmək
List<User> users = new ArrayList<>(Arrays.asList(
new User("Alisa", 17),
new User("Bob", 25),
new User("Charlie", 15)
));
users.removeIf(u -> u.age < 18);
System.out.println(users); // [Bob (25)]
5. Faydalı nüanslar
Xüsusiyyətlər: görünüş sahəsi və «effektiv final» dəyişənlər
Lambda ifadəsi xarici metoddan dəyişənlərdən istifadə edə bilər, ancaq onlar final və ya «effektiv final» olduqda (yəni ilkinləşdirmədən sonra dəyişdirilmədikdə).
int minAge = 18;
users.stream()
.filter(u -> u.age >= minAge)
.forEach(System.out::println);
Əgər siz lambda daxilində istifadə etdikdən sonra minAge dəyərini dəyişməyə cəhd etsəniz — kompilyator səhv verəcək.
Cədvəl: kolleksiya və stream metodlarının lambda ilə əsas nümunələri
| Kolleksiya/stream metodu | Nə edir | Lambda tipi | Nümunə |
|---|---|---|---|
|
Hər element üçün | |
|
|
Şərtə uyğun elementləri silir | |
|
|
Elementləri çeşidləyir | |
|
|
Hər elementi əvəz edir | |
|
|
Axını filtrlayır | |
|
|
Elementləri çevirir | |
|
|
Axın üzrə iterasiya | |
|
|
Axında çeşidləmə | |
|
7. Tipik səhvlər
Səhv №1: Lambda həddindən artıq uzundur. Əgər lambda ifadəsinin içində artıq 5 sətir kod, şərtlər, dövrlər və try-catch varsa — yəqin ki, bu kodu ayrıca metoda çıxarmaq lazımdır. Lambda-lar qısa məntiq üçün yaxşıdır.
Səhv №2: Dəyişənlərin dəyişdirilməsi. Əgər lambda daxilində xarici metodun dəyişənini (məsələn, sayğacı) dəyişməyə çalışırsınızsa, kompilyator buna icazə verməyəcək. Dəyişən final və ya effektiv final olmalıdır.
Səhv №3: Kolleksiya/stream metodlarının həmişə mənbə kolleksiyanı dəyişmədiyini unutmaq. Məsələn, stream().filter(...) ilkin siyahını dəyişmir, yeni axın qaytarır. Kolleksiya əldə etmək istəyirsinizsə — collect(Collectors.toList()) istifadə edin.
Səhv №4: Lambda ifadəsinin tipi uyğun deyil. Əgər metod, məsələn, Comparator<T> qəbul edirsə, siz isə iki parametr əvəzinə bir parametrli lambda ötürməyə çalışırsınızsa — kompilyasiya xətası olacaq.
Səhv №5: Daxili lambda-lar oxunaqlığı azaldır. Əgər sizdə map, filter, forEach zənciri var və hər bir lambda daxilində başqa bir lambda yerləşirsə — kod oxunaqsız olur. Belə hallarda ifadələri ayrı addımlara bölmək və ya hissələri metodlara çıxarmaq daha yaxşıdır.
GO TO FULL VERSION