CodeGym /Kurslar /JAVA 25 SELF /reduce və collect metodları: məlumatların aqreqasiyası

reduce və collect metodları: məlumatların aqreqasiyası

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

1. reduce metodu: universal yığma

Proqramlaşdırmada tez-tez kolleksiyanı tək yekun dəyərə “yığmaq” tələb olunur: cəmi hesablamaq, hasili tapmaq, sətirləri yapışdırmaq, aqreqasiya metrikasını çıxarmaq və ya elementləri yeni bir struktura toplamaq. Əvvəllər bu, döngələr və akkumulyator dəyişəni ilə əl ilə edilirdi. Bu gün Stream API – kodu yığcam və deklarativ yazmağa imkan verən universal reducecollect metodlarını təklif edir.

  • reduce — axını tək yekun dəyərə yığır (cəm, hasil, konkatenasiya və s.).
  • collect — axını kolleksiyaya, sətirə, xəritəyə və ya istənilən struktura çevirir.

Gəlin ardıcıllıqla baxaq.

reduce nə üçündür?

reduce — terminal metoddur; axının elementlərini akkumulyator funksiyasından istifadə edərək tək bir dəyərə “yığır”. Fikri olaraq bu, nəticənin addım-addım toplanması ilə kolleksiya üzrə keçid kimidir.

reduce metodunun imzaları

Stream API-də reduce()-un üç əsas variantı var:

Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
  • accumulator — cari yığılmış dəyəri və növbəti elementi qəbul edib yeni nəticə qaytaran funksiya.
  • identity — akkumulyatorun başlanğıc dəyəri (məsələn, cəm üçün 0, hasil üçün 1).
  • combiner — paralel axınlarda ara nəticələri birləşdirmək üçün istifadə olunur.

reduce istifadəsi nümunələri

Nümunə 1: Rəqəmlərin cəmi

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

// identity olmadan reduce: nəticə Optional
Optional<Integer> sum1 = numbers.stream()
    .reduce((a, b) -> a + b);

System.out.println(sum1.orElse(0)); // 15

// identity ilə reduce: nəticə həmişə var
int sum2 = numbers.stream()
    .reduce(0, (a, b) -> a + b);

System.out.println(sum2); // 15

Nümunə 2: Bütün rəqəmlərin hasili

int product = numbers.stream()
    .reduce(1, (a, b) -> a * b);
System.out.println(product); // 120

Nümunə 3: Sətirlərin birləşdirilməsi

List<String> words = List.of("Java", "Stream", "API");
String phrase = words.stream()
    .reduce("", (a, b) -> a + " " + b);
System.out.println(phrase.trim()); // Java Stream API

Nümunə 4: Maksimum elementi tapmaq

Optional<Integer> max = numbers.stream()
    .reduce(Integer::max);
max.ifPresent(System.out::println); // 5

Nümunə 5: Bütün sətirlərin uzunluqlarının cəmi

List<String> texts = List.of("pişik", "it", "fil");
int totalLength = texts.stream()
    .map(String::length)
    .reduce(0, Integer::sum);
System.out.println(totalLength); // 10

reduce necə işləyir

reduce məntiqi aşağıdakı döngüyə ekvivalentdir:

T result = identity;
for (T element : collection) {
    result = accumulator.apply(result, element);
}
return result;

Əgər identity verilməyibsə, başlanğıc dəyər kimi axının ilk elementi götürülür və metod Optional qaytarır (axın boşdursa, nəticə də boşdur).

2. collect metodu: universal çevirmə

collect — axını kolleksiyaya, sətirə, xəritəyə və ya hər hansı başqa struktura çevirən terminal metoddur. Bunun üçün “kollektorlar”dan (Collector) istifadə olunur; onlar yığma prosesini təsvir edir. Ən çox Collectors sinfindən hazır olanları götürülür.

Ən populyar kollektorlar

  • Collectors.toList() — elementləri List-ə toplayır.
  • Collectors.toSet() — elementləri Set-ə toplayır.
  • Collectors.toMap() — elementləri Map-ə toplayır.
  • Collectors.joining() — sətirləri tək sətirdə birləşdirir.
  • Collectors.groupingBy() — elementləri əlamətə görə qruplaşdırır.
  • Collectors.counting() — elementlərin sayını hesablayır.
  • Collectors.summarizingInt() — rəqəmlər üzrə statistik məlumatları toplayır (cəm, orta, min/maks).

collect istifadəsi nümunələri

Nümunə 1: Siyahıya toplamaq

List<String> names = List.of("Anya", "Boris", "Vasya", "Anya");
List<String> uniqueNames = names.stream()
    .distinct()
    .collect(Collectors.toList());
System.out.println(uniqueNames); // [Anya, Boris, Vasya]

Nümunə 2: Çoxluğa (Set) toplamaq

Set<String> nameSet = names.stream()
    .collect(Collectors.toSet());
System.out.println(nameSet); // [Anya, Boris, Vasya] (sıra zəmanətli deyil)

Nümunə 3: Sətrə toplamaq

String csv = names.stream()
    .collect(Collectors.joining(", "));
System.out.println(csv); // Anya, Boris, Vasya, Anya

Nümunə 4: Map-ə toplamaq

Tutaq ki, bizdə belə bir sinif var:

public class Employee {
    private String name;
    private String department;
    public Employee(String name, String department) {
        this.name = name;
        this.department = department;
    }
    public String getName() { return name; }
    public String getDepartment() { return department; }
}

“ad → şöbə” xəritəsini quraq:

List<Employee> employees = List.of(
    new Employee("Anya", "IT"),
    new Employee("Boris", "HR"),
    new Employee("Vasya", "IT")
);

Map<String, String> nameToDept = employees.stream()
    .collect(Collectors.toMap(
        Employee::getName,
        Employee::getDepartment,
        (oldValue, newValue) -> newValue // eyni adların işlənməsi
    ));

System.out.println(nameToDept); // {Anya=IT, Boris=HR, Vasya=IT}

Nümunə 5: Unikal elementləri Set-ə toplamaq

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

Nümunə 6: Rəqəmlər üzrə statistikanı toplamaq

List<Integer> numbers = List.of(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numbers.stream()
    .collect(Collectors.summarizingInt(Integer::intValue));
System.out.println(stats.getSum());     // 15
System.out.println(stats.getAverage()); // 3.0
System.out.println(stats.getMax());     // 5
System.out.println(stats.getMin());     // 1

3. Müqayisə: nə zaman reduce, nə zaman — collect

reduce — ikili əməliyyatla tək yekun dəyər əldə etmək lazım olanda: cəm, hasil, maksimum, konkatenasiya.

collect — elementləri kolleksiya/xəritə/sətrə toplamaq və ya Collector ilə daha mürəkkəb aqreqasiya aparmaq lazım olanda. Belə tapşırıqlarda collect adətən daha güclü və effektivdir.

Cədvəl: reduce vs collect

Tapşırıq Nə istifadə etməli Nümunə
Rəqəmlərin cəmi
reduce
reduce(0, Integer::sum)
Hasil
reduce
reduce(1, (a, b) -> a * b)
List-ə toplamaq
collect
collect(Collectors.toList())
Map-ə toplamaq
collect
collect(Collectors.toMap(...))
Qruplaşdırma
collect
collect(Collectors.groupingBy(...))
Sətirlərin birləşdirilməsi reduce / collect reduce("", String::concat) və ya Collectors.joining()

4. Praktiki tapşırıqlar

Tapşırıq 1: Siyahıdakı bütün sətirlərin uzunluqlarının cəmini tapın

List<String> words = List.of("pişik", "it", "fil");

int totalLength = words.stream()
    .mapToInt(String::length)
    .sum(); // yaxud reduce ilə: .reduce(0, Integer::sum)

System.out.println(totalLength); // 10

Tapşırıq 2: Unikal elementləri Set-ə toplamaq

List<String> fruits = List.of("alma", "armud", "alma", "portağal");

Set<String> uniqueFruits = fruits.stream()
    .collect(Collectors.toSet());

System.out.println(uniqueFruits); // [alma, armud, portağal]

Tapşırıq 3: Obyektlər siyahısından Map qurmaq

List<Employee> employees = List.of(
    new Employee("Anya", "IT"),
    new Employee("Boris", "HR"),
    new Employee("Vasya", "IT")
);

Map<String, String> nameToDept = employees.stream()
    .collect(Collectors.toMap(
        Employee::getName,
        Employee::getDepartment,
        (oldValue, newValue) -> newValue // adlar üst-üstə düşərsə
    ));

System.out.println(nameToDept);

Tapşırıq 4: Bütün adları vergüllə birləşdirmək

String allNames = employees.stream()
    .map(Employee::getName)
    .collect(Collectors.joining(", "));

System.out.println(allNames); // Anya, Boris, Vasya

5. Reallaşdırma xüsusiyyətləri və incəliklər

Optional və reduce

Əgər reduce-u identity olmadan istifadə etsəniz, nəticə — Optional olacaq. Bu təhlükəsizdir: axın boşdursa, nəticə də boşdur. Onu düzgün emal etməyi unutmayın: ifPresent(...), orElse(...), orElseThrow(...).

Optional<Integer> max = numbers.stream().reduce(Integer::max);
max.ifPresent(System.out::println);

Öz kollektorlar: daha çox çeviklik lazım olanda

Standartlar kifayət etməyəndə öz Collector-unuzu yaza bilərsiniz. Amma işlərin 99%-i üçün Collectors-dakı hazır kollektorlar yetərlidir.

Kollektorlar və paralel stream-lər

Collectors-dakı hazır kollektorlar parallelStream() ilə düzgün işləmək üçün layihələndirilib. Paralel axında forEach daxilində ümumi dəyişən kolleksiyaya əl ilə element əlavə etməyin — data race-lərə səbəb ola bilərsiniz.

6. reduce və collect ilə işləyərkən tipik səhvlər

Səhv №1: reduce-dən sonra Optional-ı yoxlamırsınız. Axın boşdursa, reduce identity olmadan boş Optional qaytarır. get() çağırışı NoSuchElementException-a gətirəcək. ifPresent, orElse və ya orElseThrow istifadə edin.

Səhv №2: Kolleksiyanı reduce ilə toplamağa çalışırsınız. Bu, mümkündür, amma bunun üçün collect daha uyğun və daha sürətlidir:

// Səmərəsizdir!
List<String> list = stream.reduce(
    new ArrayList<>(),
    (acc, elem) -> { acc.add(elem); return acc; },
    (acc1, acc2) -> { acc1.addAll(acc2); return acc1; }
);
// Daha yaxşısı:
List<String> list2 = stream.collect(Collectors.toList());

Səhv №3: toMap-də açar dublikatlarını emal etmirsiniz. Açarlar üst-üstə düşərsə, istisna atılacaq. Münaqişələri həll etmək üçün toMap-də üçüncü arqumenti əlavə edin.

Səhv №4: Paralel stream-lərdə sinxronizasiya olmadan dəyişən kolleksiyalardan istifadə edirsiniz. collect-də standart kollektorları istifadə edin — onlar paralel rejimdə düzgün işləyir. parallelStream() üzərində forEach-də list.add() etməyin.

Səhv №5: Mürəkkəb tapşırıqlar üçün reduce və collect-i qarışdırırsınız. reduce — sadə aqreqasiyalar (cəm, maksimum) üçün. collect — kolleksiyalara toplamaq, qruplaşdırmaq, Map qurmaq və kompleks aqreqasiya üçün.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Həftə ərzində pik temperaturun tapılması 🌡️
Həftə ərzində pik temperaturun tapılması 🌡️
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Sehrli sehrin yaradılması 📜
Sehrli sehrin yaradılması 📜
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION