CodeGym /Kurslar /JAVA 25 SELF /flatMap və mapMulti metodları

flatMap və mapMulti metodları

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

1. Giriş

Təsəvvür edək ki, bizdə tələbələrin siyahısı var və hər bir tələbənin məşğuliyyətləri (hobbiləri) siyahısı mövcuddur. Məsələn:

List<List<String>> hobbies = List.of(
    List.of("Üzgüçülük", "Şahmat"),
    List.of("Futbol"),
    List.of("Proqramlaşdırma", "Oxu", "Kino")
);

Tapşırıq: bütün hobbilərin vahid siyahısını almaq ki, tələbələrin ümumiyyətlə nələrlə maraqlandığını görək. Məntiqi olaraq map tətbiq etməyə cəhd edək:

List<Stream<String>> streams = hobbies.stream()
    .map(list -> list.stream())
    .collect(Collectors.toList());

Nə əldə etdik? Axınların siyahısı! Yəni Stream<Stream<String>>. Bu elə də lazım olan deyil — biz sadəcə Stream<String> almaq istəyirik ki, hər hobbiyə birbaşa işləyək.

Təsəvvür edin ki, sizin bir qutunuz var və onun içində oyuncaqlarla dolu başqa qutular var. map metodu sadəcə hər qutunu çıxarıb sizə qutunu göstərir (Stream<Stream<String>>). Siz isə qutuları qurdalamadan dərhal bütün oyuncaqları görmək istəyirsiniz (Stream<String>).

2. flatMap: iç‑içə kolleksiyaları necə “açmaq”

“Qutuları açıb” düz axın əldə etmək üçün flatMap lazımdır.
O, hər element üçün axın qaytaran funksiyanı qəbul edir və bütün daxili axınları dərhal bir axında “yastılaşdırır”.

flatMap sintaksisi

Stream<T> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

Sadə desək: flatMap hər element üçün bir axın (Stream) qaytarmağınızı gözləyir və onların hamısını bir böyük axında “hamarlayır”.

Nümunə: tələbələrin bütün hobbilərini birləşdirmək

List<String> allHobbies = hobbies.stream()
    .flatMap(list -> list.stream())
    .collect(Collectors.toList());

System.out.println(allHobbies);
// [Üzgüçülük, Şahmat, Futbol, Proqramlaşdırma, Oxu, Kino]

Hər hobbilər siyahısı üçün list.stream() çağırdıq (bir tələbənin hobbiləri axını). Bəs flatMap bütün axınları bir böyük axında “hamarladı” — indi biz hobbilərin özünü görürük, hobbilər axınını yox.

Vizual sxem

hobbies.stream()
    |
    |---> [Üzgüçülük, Şahmat]    -> stream
    |---> [Futbol]               -> stream
    |---> [Proqramlaşdırma, ...] -> stream
    |
flatMap: hamısını bir Stream<String>-də birləşdirir

Niyə map məsələni həll etmir?

Əgər biz map istifadə etsəydik, Stream<Stream<String>> alardıq və bununla işləmək əlverişli deyil: məsələn, bütün sətrləri dərhal dolaşmaq mümkün olmur, əlavə keçidlər gərəkir.

3. flatMap‑in praktik istifadəsi nümunələri

Sətirləri simvollara bölmək (List<String> → Stream<Character>)

Tutaq ki, sizdə sətirlər siyahısı var və bütün sətirlərdə rast gəlinən bütün simvolların axınını almaq istəyirsiniz:

List<String> words = List.of("Java", "Stream");

List<Character> characters = words.stream()
    .flatMap(word -> word.chars().mapToObj(ch -> (char) ch))
    .collect(Collectors.toList());

System.out.println(characters);
// [J, a, v, a, S, t, r, e, a, m]

İzah:

  • word.chars() IntStream qaytarır (simvol kodlarının axını).
  • mapToObj kodları simvollara çevirir.
  • flatMap alınan bütün simvol axınlarını bir axında birləşdirir.

Optional ilə iş: Stream<Optional<T>> → Stream<T>

Tutaq ki, sizdə Optional obyektlərinin siyahısı var və yalnız həqiqətən mövcud olan dəyərlərin axınını almaq istəyirsiniz:

List<Optional<String>> optionals = List.of(
    Optional.of("Java"),
    Optional.empty(),
    Optional.of("Stream")
);

List<String> present = optionals.stream()
    .flatMap(opt -> opt.stream())
    .collect(Collectors.toList());

System.out.println(present);
// [Java, Stream]

Fənd:
Optional<T> (Java 9‑dan etibarən) stream() metoduna malikdir: ya boş axın, ya da bir elementdən ibarət axın qaytarır. flatMap isə bütün boş olmayan dəyərləri bir axında birləşdirir.

4. Yeni mapMulti metodu: flatMap lazım olmadıqda

mapMulti niyə yarandı?

Java 16‑da əlavə olunan mapMulti metodu flatMap-ə bənzər işləyir, amma bir qədər daha səmərəli və çevikdir.

flatMap güclü alətdir, amma bir çatışmazlığı var: hər element üçün yeni Stream yaratmağa məcbursunuz — hətta 0, 1 və ya bir neçə element qaytarmaq istəsəniz belə. Bu, xüsusən də aralıq kolleksiya və ya axınlar yaratmadan elementləri sadəcə “açmaq” istədikdə səmərəsiz ola bilər.

mapMultiflatMap-in təkmilləşdirilmiş variantıdır: aralıq strukturlar yaratmadan lazımi dəyərləri nəticə axınına Consumer vasitəsilə birbaşa əlavə etməyə imkan verir. Axın qaytarmaq əvəzinə, Consumer vasitəsilə nəticə axınına hansı elementləri əlavə edəcəyinizi birbaşa göstərirsiniz.

mapMulti sintaksisi

<R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper)

Hər element üçün mapper funksiyası çağırılır; o, elementin özünü və yeni dəyərləri “yığa” biləcəyiniz Consumer-i qəbul edir.

Nümunə: filtrasiya və elementlərin bir keçiddə açılması

Tutaq ki, sizdə ədədlər siyahısı var və cüt ədədləri axına iki dəfə, tək ədədləri isə heç əlavə etməmək istəyirsiniz (yəni eyni anda həm filtrasiya, həm də “açma”):

List<Integer> numbers = List.of(1, 2, 3, 4);

List<Integer> result = numbers.stream()
    .mapMulti((number, consumer) -> {
        if (number % 2 == 0) {
            consumer.accept(number);
            consumer.accept(number); // İki dəfə əlavə edirik
        }
        // Tək ədədlər üçün heç nə etmirik (filtrləmə)
    })
    .collect(Collectors.toList());

System.out.println(result);
// [2, 2, 4, 4]

flatMap ilə müqayisə

Eyni vəzifəni flatMap ilə belə yazardınız:

List<Integer> result = numbers.stream()
    .flatMap(number -> number % 2 == 0
        ? Stream.of(number, number)
        : Stream.empty())
    .collect(Collectors.toList());

Burada hər bir element üçün yenə də Stream.of(...) və ya Stream.empty() yaratmağa məcburuq, bu isə qeyri‑səmərəli ola bilər.

5. flatMap nə vaxt, mapMulti nə vaxt?

  • map — hər elementi bir elementə çevirdikdə.
  • flatMap — hər elementi elementlər axınına çevirdikdə.
  • mapMulti — aralıq axın yaratmadan bir neçə elementi hasil etmək istədikdə (daha səmərəli, xüsusən “isti” dövrlərdə).

Daha bir nümunə: Map<Integer, List<String>>-i Stream<Pair(Integer, String)> şəklinə açmaq

Tutaq ki, bizdə xəritə var: id → hobbilər siyahısı.

Map<Integer, List<String>> studentHobbies = Map.of(
    1, List.of("Üzgüçülük", "Şahmat"),
    2, List.of("Futbol"),
    3, List.of("Proqramlaşdırma", "Oxu", "Kino")
);

List<String> allHobbies = studentHobbies.values().stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList());

System.out.println(allHobbies);
// [Üzgüçülük, Şahmat, Futbol, Proqramlaşdırma, Oxu, Kino]

Əgər biz (id, hobi) cütlərini almaq istəsək:

List<String> pairs = studentHobbies.entrySet().stream()
    .flatMap(entry -> entry.getValue().stream()
        .map(hobby -> entry.getKey() + ": " + hobby))
    .collect(Collectors.toList());

System.out.println(pairs);
// [1: Üzgüçülük, 1: Şahmat, 2: Futbol, 3: Proqramlaşdırma, 3: Oxu, 3: Kino]

mapMulti ilə (Java 16+):

List<String> pairs = studentHobbies.entrySet().stream()
    .mapMulti((entry, consumer) -> {
        for (String hobby : entry.getValue()) {
            consumer.accept(entry.getKey() + ": " + hobby);
        }
    })
    .collect(Collectors.toList());

Üstünlük:
mapMulti aralıq axınlar yaratmır, dəyərləri birbaşa yekun axına “atır”.

Vizualizasiya: flatMap və mapMulti

Metod Funksiya nə qaytarır Necə birləşdirir Aralıq kolleksiyalar?
map
Bir element Sadəcə Yox
flatMap
Axın (Stream) Birləşdirir Bəli (aralıq Stream-lər)
mapMulti
Consumer (0..n dəfə) Birləşdirir Yox (birbaşa əlavə edir)

6. flatMap və mapMulti ilə işləyərkən tipik səhvlər

Səhv №1: Stream<T> əvəzinə Stream<Stream<T>> alındı. Tez-tez tələbələr kolleksiyaların kolleksiyaları ilə işləyərkən map əvəzinə flatMap istifadə edirlər. Nəticədə artıq dövrlər yazmalı olurlar.

Səhv №2: Qaytarılan dəyərin tipi yanlışdır. flatMap üçün funksiya Stream qaytarmalıdır, List və ya massiv yox.

Səhv №3: Qeyri‑səmərəlilik. Sadə hallarda flatMap əladır, lakin hər element üçün Stream.of və ya Stream.empty() yaratmaq lazım gəlirsə, bu artıq ola bilər. Belə tapşırıqlar üçün mapMulti daha uyğundur.

Səhv №4: mapMulti Java‑nın köhnə versiyalarında işləmir. mapMulti yalnız Java 16-da peyda olub. Sizdə daha köhnə JDK varsa, bu metod mövcud olmayacaq.

Səhv №5: null ilə problemlər. flatMap-dən heç vaxt null qaytarmayın — “boş” hallar üçün həmişə Stream.empty() qaytarın.

Səhv №6: Aralıq kolleksiyalar. Əgər elementləri birbaşa mapMulti-də consumer vasitəsilə əlavə etmək mümkündürsə, artıq siyahılar və ya axınlar yaratmayın.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Məktəb klubları üçün tələbələrin bütün məşğuliyyətlərinin toplanması 📚
Məktəb klubları üçün tələbələrin bütün məşğuliyyətlərinin toplanması 📚
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Hər tələbə üçün hobbi kataloqunun yaradılması 🧑‍🎓
Hər tələbə üçün hobbi kataloqunun yaradılması 🧑‍🎓
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION