CodeGym /Kurslar /JAVA 25 SELF /Təkmil kollektorlar

Təkmil kollektorlar

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

1. Giriş

Stream API ilə kolleksiyalar üzərində işləyərkən, çox vaxt elementləri sadəcə qruplaşdırmaq deyil, eyni zamanda onlarla nəsə etmək lazım olur: çevirmək, filtr etmək, başqa tip kolleksiyaya yığmaq. Bunun üçün Java-da downstream kollektorlar var — hər bir qrup və ya verilənlər hissəsinə tətbiq olunan iç-içə kollektorlar.

Downstream kollektor nədir?

Bu, qruplaşdırma və ya bölmə nəticəsinə tətbiq olunan kollektorudur. Məsələn, groupingBy ilə tələbələri kursa görə qruplaşdıra və hər qrup daxilində yalnız adları və ya yalnız fərqlənənləri (məsələn, GPA üzrə 4.5 həddi ilə) yığa bilərsiniz.

Nümunələr

mapping

Qruplardakı elementləri yığmadan əvvəl çevirməyə imkan verir.

Map<Integer, List<String>> namesByCourse = students.stream()
    .collect(Collectors.groupingBy(
        Student::getCourse,
        Collectors.mapping(Student::getName, Collectors.toList())
    ));
  • Tələbələri kursa görə qruplaşdırırıq.
  • Hər qrup üçün yalnız adları yığırıq (bütöv obyektləri deyil).

filtering

Qrup daxilində elementləri filtr etməyə imkan verir (məsələn, GPA >= 4.5 qalsın).

Map<Integer, List<Student>> honorsByCourse = students.stream()
    .collect(Collectors.groupingBy(
        Student::getCourse,
        Collectors.filtering(s -> s.getGpa() >= 4.5, Collectors.toList())
    ));

Hər qrupda yalnız fərqlənənləri saxlayırıq.

flatMapping

Qruplar daxilində iç-içə kolleksiyaları “açmağa” imkan verir.

Map<String, Set<String>> tagsByAuthor = books.stream()
    .collect(Collectors.groupingBy(
        Book::getAuthor,
        Collectors.flatMapping(
            book -> book.getTags().stream(),
            Collectors.toSet()
        )
    ));

Hər müəllif üçün onun bütün kitablarının unikal teqlərini yığırıq.

partitioningBy ilə downstream

groupingBy-a bənzər işləyir, lakin boolean şərtinə əsasən iki qrupa ayırır.

Map<Boolean, List<String>> namesByPassed = students.stream()
    .collect(Collectors.partitioningBy(
        s -> s.getGpa() >= 3.0,
        Collectors.mapping(Student::getName, Collectors.toList())
    ));

Tələbələri uğurla keçənlər və keçməyənlər olaraq bölürük, hər qrupda isə yalnız adlar qalır.

2. teeing: eyni vaxtda iki kollektorla aqreqasiya

Bəzən axın üzrə eyni anda bir neçə aqreqatı hesablamaq lazımdır: məsələn, həm cəmi, həm də ortanı, yaxud minimum və maksimumu. Bunun üçün Java 12+-də teeing kollektoru peyda oldu.

teeing necə işləyir?

Siz iki kollektor və onların nəticələrini birləşdirən funksiyanı verirsiniz.

Sintaksis:

Collectors.teeing(collector1, collector2, (result1, result2) -> ...)

Nümunələr

Minimum + maksimum

Optional<MinMax> minMax = numbers.stream()
    .collect(Collectors.teeing(
        Collectors.minBy(Integer::compareTo),
        Collectors.maxBy(Integer::compareTo),
        (min, max) -> min.isPresent() && max.isPresent() ? new MinMax(min.get(), max.get()) : null
    ));

Minimum və maksimumu eyni anda tapırıq.

Cəm + orta

Result result = numbers.stream()
    .collect(Collectors.teeing(
        Collectors.summingInt(Integer::intValue),
        Collectors.averagingInt(Integer::intValue),
        (sum, avg) -> new Result(sum, avg)
    ));

Cəm və orta dəyər olan bir obyekt alırıq.

Nümunə: maaşlar üzrə hesabat

SalaryStats stats = employees.stream()
    .collect(Collectors.teeing(
        Collectors.summingInt(Employee::getSalary),
        Collectors.averagingInt(Employee::getSalary),
        SalaryStats::new
    ));

SalaryStats daxilində həm cəmi, həm də ortanı saxlayırıq.

3. toUnmodifiableList/Set/Map və kolleksiyanı “dondurmaq” üçün collectingAndThen

Java-nın müasir versiyalarında yaradıldıqdan sonra dəyişdirilə bilməyən — dəyişdirilə bilməyən (unmodifiable) kolleksiyalar mövcuddur. Bu, nəticənin təsadüfən dəyişdirilməməsinin vacib olduğu API-lər üçün rahatdır.

toUnmodifiableList/Set/Map

  • Dəyişdirilə bilməyən kolleksiya qaytarırlar.
  • Hər hansı element əlavə/çıxarma cəhdi UnsupportedOperationException atacaq.

Nümunələr:

List<String> names = students.stream()
    .map(Student::getName)
    .collect(Collectors.toUnmodifiableList());
Map<Integer, Student> byId = students.stream()
    .collect(Collectors.toUnmodifiableMap(Student::getId, Function.identity()));

collectingAndThen

Kollektorun nəticəsinə funksiyanı tətbiq etməyə imkan verir — məsələn, kolleksiyanı “dondurmaq”.

List<String> names = students.stream()
    .map(Student::getName)
    .collect(Collectors.collectingAndThen(
        Collectors.toList(),
        Collections::unmodifiableList
    ));

Əvvəl adi siyahıya yığırıq, sonra onu dəyişdirilə bilməyən edirik.

Set ilə nümunə:

Set<String> tags = books.stream()
    .flatMap(book -> book.getTags().stream())
    .collect(Collectors.collectingAndThen(
        Collectors.toSet(),
        Set::copyOf // Java 10+
    ));

4. Piplayn keisləri

Kəsimlər üzrə hesabatlar və statistika

Təkmil kollektorların köməyi ilə mürəkkəb hesabat və statistikanı “bir sətirdə” qurmaq olar.

Nümunə: şöbələr üzrə orta maaş

Map<String, Double> avgSalaryByDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.averagingInt(Employee::getSalary)
    ));

Nümunə: kateqoriyalar üzrə ən bahalı ilk 3 məhsul

Map<String, List<Product>> top3ByCategory = products.stream()
    .collect(Collectors.groupingBy(
        Product::getCategory,
        Collectors.collectingAndThen(
            Collectors.toList(),
            list -> list.stream()
                        .sorted(Comparator.comparing(Product::getPrice).reversed())
                        .limit(3)
                        .toList()
        )
    ));

API müqaviləsi kimi dəyişdirilə bilməyən nəticə

Əgər metodunuz dəyişdirilə bilməyən kolleksiya qaytarırsa, bu, təsadüfi səhvlərdən qoruyur və API-ni daha etibarlı edir.

public List<String> getTags() {
    return tags.stream()
        .collect(Collectors.toUnmodifiableList());
}

İstifadəçi getTags().add("yeni teq") edə bilməyəcək — istisna atılacaq.

Nümunə: bir neçə aqreqatlı hesabat (teeing)

public SalaryReport getSalaryReport(List<Employee> employees) {
    return employees.stream()
        .collect(Collectors.teeing(
            Collectors.averagingInt(Employee::getSalary),
            Collectors.summingInt(Employee::getSalary),
            SalaryReport::new
        ));
}

SalaryReport daxilində həm orta, həm də cəmi maaşı saxlayırıq.

5. Tipik səhvlər və incəliklər

Səhv №1: Dəyişdirilməzliyi unutmaq. Əgər adi siyahı qaytarırsınızsa, onu dəyişə bilərlər. Nəticəni “dondurmaq” üçün toUnmodifiableList/Set/Map və ya collectingAndThen istifadə edin.

Səhv №2: Yanlış downstream kollektor. Qrup daxilində elementləri çevirmək lazımdırsa — mapping; filtr etmək lazımdırsa — filtering; iç-içə kolleksiyaları “açmaq” lazımdırsa — flatMapping.

Səhv №3: UnsupportedOperationException. toUnmodifiableList/Set/Map ilə yığılmış və ya collectingAndThen vasitəsilə “dondurulmuş” kolleksiyanı dəyişdirməyə cəhd edərkən yaranır.

Səhv №4: Unikallığın itirilməsi/açarların toqquşması. toUnmodifiableSet unikallıq tələb edir, toUnmodifiableMap isə unikal açarları; əks halda yığma zamanı istisna alacaqsınız.

1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Qruplaşdırma və aqreqasiya
Stream API: qruplaşdırma və aqreqasiya
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION