CodeGym /Kurslar /JAVA 25 SELF /Kolleksiyaların Stream vasitəsilə çevrilməsi

Kolleksiyaların Stream vasitəsilə çevrilməsi

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

1. List → Set və əksinə çevrilmə

Gəlin, ümumiyyətlə nə üçün kolleksiyaları çevirdiyimizi xatırlayaq. Real tapşırıqlarda tez-tez bizə lazım olur:

  • Siyahıdan unikal elementləri almaq (məsələn, e‑mail siyahısı → unikal ünvanlar dəsti).
  • Xəritə (Map) qurmaq, məsələn, adlar siyahısından «ad → adın uzunluğu» xəritəsi əldə etmək.
  • Elementləri tək bir sətirdə birləşdirmək (məsələn, səliqəli çıxış üçün).

Əvvəllər bunun üçün dövrlər, şərtlər və müvəqqəti kolleksiyalarla çox kod yazmaq lazım gəlirdi. Stream API ilə hər şey daha sadə və… daha üslublu oldu!

Nümunə: siyahıdan unikal adlar dəsti almaq

Tutaq ki, bizdə adlar siyahısı var (birdən proqramınızda kimsə özünü iki dəfə daxil edib — olur belə!):

List<String> names = List.of("Anna", "Sergey", "Anna", "Mariya", "Ivan", "Sergey");

Məqsədimiz — hər bir adın yalnız bir dəfə göründüyü kolleksiya, yəni dəst (Set) əldə etməkdir. Stream API ilə bunu sözün əsl mənasında bir sətrə etmək olar:

Set<String> uniqueNames = names.stream()
    .collect(Collectors.toSet());
System.out.println(uniqueNames);

Çıxış:

[Mariya, Ivan, Anna, Sergey]

(Set-də sıra zəmanətli deyil — təəccüblənməyin, sizdə başqa cür ola bilər.)

Bəs əksinə lazımdırsa: Set → List?

Bəzən əksinə — dəsti siyahıya çevirmək lazımdır (məsələn, çeşidləmək və ya indekslə müraciət etmək üçün):

List<String> namesList = uniqueNames.stream()
    .collect(Collectors.toList());
System.out.println(namesList);

2. Map-ə çevrilmə: Collectors.toMap()

Nümunə: adlar siyahısından «ad → adın uzunluğu» Map yaratmaq

Bəzən sadəcə proqramçı yox, əsl xəritəçi olmaq istəyirsən — xəritələr qurursan! Gəlin yoxlayaq:

List<String> names = List.of("Anna", "Sergey", "Mariya", "Ivan");

Map<String, Integer> nameToLength = names.stream()
    .collect(Collectors.toMap(
        name -> name,         // açar — adın özü
        name -> name.length() // dəyər — adın uzunluğu
    ));

System.out.println(nameToLength);

Çıxış:

{Mariya=5, Ivan=4, Anna=4, Sergey=6}

Vacib məqam: açar dublikatları

Əgər ilkin siyahıda eyni adlar varsa, onları Map-ə toplamağa cəhd edəndə IllegalStateException: Duplicate key xətası yaranacaq. Java siz eyni açarla iki dəyər qoymaq istəyəndə bunu xoşlamır.

Dublikatları necə işləməli?
Açarlar üst-üstə düşəndə nə etmək lazım olduğunu göstərə bilərsiniz — məsələn, ilk dəyəri və ya son dəyəri saxlamaq:

List<String> names = List.of("Anna", "Sergey", "Anna", "Mariya", "Ivan", "Sergey");

Map<String, Integer> nameToLength = names.stream()
    .collect(Collectors.toMap(
        name -> name,
        name -> name.length(),
        (oldValue, newValue) -> oldValue // ilk dəyəri saxlamaq
    ));

System.out.println(nameToLength);

Artıq proqram çökməyəcək və Map-ə hər adın yalnız ilk daxilolması düşəcək.

Nümunə: obyektlərlə Map

Bir az mürəkkəbləşdirək: bizdə istifadəçilər siyahısı var və «ad → istifadəçi» Map qurmaq istəyirik:

class User {
    String name;
    int age;
    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return name + " (" + age + ")";
    }
}

// İstifadəçilər siyahısına nümunə
List<User> users = List.of(
    new User("Anna", 25),
    new User("Sergey", 30),
    new User("Mariya", 22)
);

Map<String, User> nameToUser = users.stream()
    .collect(Collectors.toMap(
        user -> user.name,
        user -> user
    ));

System.out.println(nameToUser);

Çıxış:

{Mariya=Mariya (22), Anna=Anna (25), Sergey=Sergey (30)}

3. Sətirə yığma: Collectors.joining()

Bəzən təkcə kolleksiya toplamaq yox, istifadəçiyə və ya log-a səliqəli sətir vermək istəyirsən. Məsələn, bütün adları vergüllə birləşdirmək:

List<String> names = List.of("Anna", "Sergey", "Mariya", "Ivan");

String result = names.stream()
    .collect(Collectors.joining(", "));

System.out.println(result);

Çıxış:

Anna, Sergey, Mariya, Ivan

Prefiks və suffiks əlavə etmək olar

String result = names.stream()
    .collect(Collectors.joining(", ", "Siyahı: [", "]"));

System.out.println(result);

Çıxış:

Siyahı: [Anna, Sergey, Mariya, Ivan]

4. Terminal əməliyyatlar: forEach, collect, count, anyMatch, allMatch, noneMatch

forEach metodu

forEach ilə artıq yaxşı tanışıq: bu əməliyyat axının hər elementi üçün hərəkət icra edir.

names.stream().forEach(name -> System.out.println("Salam, " + name + "!"));

collect metodu

Elementləri kolleksiyaya, sətirə və ya digər struktura yığır. Ən çox istifadə olunan əməliyyat — Collectors.toList()Collectors.toSet() ilə List və ya Set-ə yığmaqdır.

count metodu

Axındakı elementlərin sayını hesablayır.

long count = names.stream()
    .filter(name -> name.length() > 4)
    .count();
System.out.println("4 hərfdən uzun adların sayı: " + count);

anyMatch, allMatch, noneMatch metodları

Şərtin heç olmasa bir element üçün (anyMatch), hamısı üçün (allMatch) və ya heç biri üçün (noneMatch) ödənilib-ödənilmədiyini yoxlayır.

boolean hasShortName = names.stream()
    .anyMatch(name -> name.length() < 4);
System.out.println("Qısa ad var? " + hasShortName);

boolean allLong = names.stream()
    .allMatch(name -> name.length() > 3);
System.out.println("Bütün adlar 3 hərfdən uzundur? " + allLong);

boolean noneIvan = names.stream()
    .noneMatch(name -> name.equals("Ivan"));
System.out.println("Ivan yoxdur? " + noneIvan);

Çıxış:

Qısa ad var? false
Bütün adlar 3 hərfdən uzundur? true
Ivan yoxdur? false

5. Terminal və ara əməliyyatlar: anlayışları möhkəmləndirək

Ara əməliyyatlar (filter, map, distinct, sorted, limit, skip, peek) — yeni Stream qaytarır, zəncirlər qurmaq olar.

Terminal əməliyyatlar (forEach, collect, count, anyMatch, allMatch, noneMatch, reduce, findFirst, findAny) — axını tamamlayır, daha nəticə olmayacaq!

Zəncir nümunəsi:

List<String> result = users.stream()
    .filter(user -> user.age > 20)
    .map(user -> user.name.toUpperCase())
    .distinct()
    .sorted()
    .collect(Collectors.toList());

System.out.println(result);

Çıxış:

[ANNA, IVAN, MARIYA, SERGEY]

6. Stream vasitəsilə kolleksiyaları çevirmədə tipik səhvlər

Səhv № 1: toMap-da açar dublikatlarının işlənməməsi
İlkin kolleksiyada təkrarlanan açarlar varsa və siz Collectors.toMap()-ı açıq birləşdirmə funksiyası göstərmədən istifadə edirsinizsə, proqram istisna atacaq. Belə hallar üçün həmişə birləşdirmə funksiyasını göstərin:

// Son dəyəri saxlamaq
.toMap(keyMapper, valueMapper, (oldVal, newVal) -> newVal)

Səhv № 2: forEach əvəzinə collect istifadə etmək
Bəzən yeni başlayanlar kolleksiyanı forEach ilə “toplamağa” çalışırlar, məsələn:

List<String> list = new ArrayList<>();
names.stream().forEach(name -> list.add(name)); // İşləyir, amma bu Stream-üsulu deyil!

Daha yaxşısı collect(Collectors.toList()) istifadə etməkdir — bu, daha təhlükəsiz və daha təmizdir.

Səhv № 3: axını təkrar istifadə etmək cəhdi
Axını yalnız bir dəfə istifadə etmək olar. Terminal əməliyyatından (məsələn, collect, forEach) sonra həmin Stream ilə işləməyə cəhd IllegalStateException ilə nəticələnəcək.

Səhv № 4: «yan təsirlər yoxdur» prinsipinin pozulması
Ara əməliyyatlar “təmiz” olmalıdır (xarici dəyişənləri dəyişdirmədən). map və ya filter daxilində axından kənarda nəyisə dəyişmək olmaz.

Səhv № 5: SetMap-də qaydanın nəzərə alınmaması
Sıra vacibdirsə, uyğun kolleksiyalardan istifadə edin — məsələn, LinkedHashSet, TreeMap — və lazım olan kollektoru göstərin.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Konfransın Unikal İştirakçılarının Hesablanması 🧑‍🤝‍🧑
Konfransın Unikal İştirakçılarının Hesablanması 🧑‍🤝‍🧑
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
İstifadəçi profillərinin siyahısının yaradılması — ilk qeydin üstünlüyü 👤
İstifadəçi profillərinin siyahısının yaradılması — ilk qeydin üstünlüyü 👤
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Stream API-nin əsasları
Stream API-nin əsasları
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION