CodeGym /Kurslar /JAVA 25 SELF /Kolleksiyalarda və stream-lərdə lambda istifadəsi

Kolleksiyalarda və stream-lərdə lambda istifadəsi

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

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ə
forEach(Consumer<T>)
Hər element üçün
x -> ...
list.forEach(s -> ...)
removeIf(Predicate<T>)
Şərtə uyğun elementləri silir
x -> ...
list.removeIf(s -> ...)
sort(Comparator<T>)
Elementləri çeşidləyir
(a, b) -> ...
list.sort((a, b) -> ...)
replaceAll(UnaryOperator<T>)
Hər elementi əvəz edir
x -> ...
list.replaceAll(s -> ...)
filter(Predicate<T>)
Axını filtrlayır
x -> ...
stream.filter(s -> ...)
map(Function<T, R>)
Elementləri çevirir
x -> ...
stream.map(s -> ...)
forEach(Consumer<T>)
Axın üzrə iterasiya
x -> ...
stream.forEach(s -> ...)
sorted(Comparator<T>)
Axında çeşidləmə
(a, b) -> ...
stream.sorted((a, b) -> ...)

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.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION