CodeGym /Kurslar /JAVA 25 SELF /Stream API alt çoxluqları: distinct, limit, skip

Stream API alt çoxluqları: distinct, limit, skip

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

1. Metod distinct: dublikatları silirik

Praktiki tapşırıqlarda tez-tez təkcə məlumatı filtrələmək və çevirmək deyil, həm də kolleksiyadan yalnız unikal elementləri seçmək, nəticənin ölçüsünü məhdudlaşdırmaq və ya əksinə, ilk bir neçə elementi ötürmək tələb olunur. Məsələn:

  • İstifadəçilərin unikal adlarının siyahısını almaq.
  • Səhifədə göstərmək üçün yalnız ilk 10 qeydi götürmək.
  • İlk 5 elementi ötürmək (məsələn, səhifələmə zamanı — “2‑ci səhifədən göstərmək”).

Belə tapşırıqlar üçün Stream API-də xüsusi metodlar var: distinct, limit, skip.

distinct necə işləyir?

distinct() metodu bütün dublikat elementləri silinmiş yeni bir axın (stream) qaytarır. Dublikatlar müvafiq sinfin equalshashCode metodları ilə müəyyən edilir.

Təsəvvür edin ki, siz numizmatsınız və əsas kolleksiyada dublikatların olmaması vacibdir. Bütün təkrar sikkələr mübadilə fonduna gedəcək. Məhz distinct() əsas kolleksiyadan dublikatları ayırıb çıxarmağa qadirdir.

Nümunə: istifadəçi adlarının unikal siyahısı

List<String> names = List.of(
    "Alisa", "Bob", "Alisa", "Eva", "Bob", "Denis", "Gleb", "Eva"
);

Unikal adların siyahısını alaq:

List<String> uniqueNames = names.stream()
    .distinct()
    .collect(Collectors.toList());

System.out.println(uniqueNames);
// Çıxış: [Alisa, Bob, Eva, Denis, Gleb]

Nümunə: istifadəçilərin unikal email-ləri

Tutaq ki, bizdə User sinfi var:

public class User {
    String name;
    String email;

    // Konstruktor, getter-lər, toString() — rahatlıq üçün
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    @Override
    public String toString() {
        return name + " <" + email + ">";
    }
}

Dublikat email-ləri olan istifadəçilərin siyahısı:

List<User> users = List.of(
    new User("Alisa", "alice@mail.com"),
    new User("Bob", "bob@mail.com"),
    new User("Eva", "eva@mail.com"),
    new User("Alisa2", "alice@mail.com"), // email dublikatı!
    new User("Gleb", "gleb@mail.com"),
    new User("Eva2", "eva@mail.com")      // email dublikatı!
);

Əgər sadəcə users.stream().distinct() çağırsanız, dublikatlar silinməyəcək, çünki User obyektlərində standart olaraq equalshashCode metodları override edilməyib. Bu halda distinct yalnız istinad səviyyəsində üst-üstə düşmələr üçün işləyir.

Həll: equalshashCode metodlarını elə override edin ki, unikalıq email-ə görə müəyyən olunsun.

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    User user = (User) o;
    return Objects.equals(email, user.email);
}

@Override
public int hashCode() {
    return Objects.hash(email);
}

İndi:

List<User> uniqueUsers = users.stream()
    .distinct()
    .collect(Collectors.toList());

uniqueUsers.forEach(System.out::println);
// Alisa <alice@mail.com>
// Bob <bob@mail.com>
// Eva <eva@mail.com>
// Gleb <gleb@mail.com>

Vacib: Öz sinifləriniz üçün equalshashCode-u həmişə override edin, əgər distinct-in normal işləməsini istəyirsiniz!

2. Metod limit: element sayını məhdudlaşdırırıq

limit(long maxSize) metodu başlanğıc axının ilk maxSize elementindən çox olmayan yeni axın qaytarır.

Analoji: Şirniyyat mağazasına gəldiniz və 100-dən yalnız 3 pirojqini dadmaq istəyirsiniz. limit(3) — sizə dəqiq ilk üçü verirlər, qalanları — “təəssüf, bu gün yox”.

Nümunə: ilk 3 ad

List<String> firstThree = names.stream()
    .limit(3)
    .collect(Collectors.toList());

System.out.println(firstThree);
// Çıxış: [Alisa, Bob, Alisa]

Tətbiqimizdən nümunə: top‑3 yeni istifadəçi

List<User> firstUsers = users.stream()
    .limit(3)
    .collect(Collectors.toList());

firstUsers.forEach(System.out::println);
// Alisa <alice@mail.com>
// Bob <bob@mail.com>
// Eva <eva@mail.com>

Sıralama ilə nümunə

Sıralama ilə kombinə etmək olar — məsələn, ən qısa email-ə malik top‑2 istifadəçini götürmək:

List<User> top2ShortEmail = users.stream()
    .sorted(Comparator.comparingInt(u -> u.email.length()))
    .limit(2)
    .collect(Collectors.toList());

top2ShortEmail.forEach(System.out::println);
// Bob <bob@mail.com>
// Eva <eva@mail.com>

3. Metod skip: ilk elementləri ötürürük

skip(long n) metodu başlanğıc axının ilk n elementi ötürülmüş yeni axın qaytarır.

Nümunə: ilk 2 adı ötürmək

List<String> afterTwo = names.stream()
    .skip(2)
    .collect(Collectors.toList());

System.out.println(afterTwo);
// Çıxış: [Alisa, Eva, Bob, Denis, Gleb, Eva]

Nümunə: səhifələmə (pagination)

Saytda çox vaxt, məsələn, “səhifə başına 3 istifadəçi” göstərmək lazımdır. İkinci səhifə üçün ilk 3-ü ötürmək, növbəti 3-ü götürmək lazımdır:

int pageSize = 3;
int pageNumber = 2; // İkinci səhifə

List<User> page = users.stream()
    .skip(pageSize * (pageNumber - 1))
    .limit(pageSize)
    .collect(Collectors.toList());

page.forEach(System.out::println);
// Alisa2 <alice@mail.com>
// Gleb <gleb@mail.com>
// Eva2 <eva@mail.com>

4. distinct, limit, skip kombinasiyası

Bu metodları tapşırıqdan asılı olaraq kombinə etmək olar.

Nümunə: ardıcıllıqda üçüncüdən başlayaraq 2 unikal adı almaq

List<String> result = names.stream()
    .distinct() // Dublikatları çıxarırıq: [Alisa, Bob, Eva, Denis, Gleb]
    .skip(2)    // Alisa və Bob-u ötürürük: [Eva, Denis, Gleb]
    .limit(2)   // Yalnız ikisini götürürük: [Eva, Denis]
    .collect(Collectors.toList());

System.out.println(result);
// Çıxış: [Eva, Denis]

Filtrləmə ilə nümunə

Tutaq ki, tərkibində “a” hərfi olan ilk 2 unikal email-i almaq lazımdır:

List<String> emails = users.stream()
    .map(user -> user.email)
    .filter(email -> email.contains("a"))
    .distinct()
    .limit(2)
    .collect(Collectors.toList());

System.out.println(emails);
// Çıxış: [alice@mail.com, eva@mail.com]

5. Praktika: tətbiq tapşırıqları

Tapşırıq 1. Uzunluğu 3 simvoldan böyük olan istifadəçi adlarının unikal siyahısını almaq

List<String> longUniqueNames = names.stream()
    .filter(name -> name.length() > 3)
    .distinct()
    .collect(Collectors.toList());

System.out.println(longUniqueNames);
// Məsələn: [Alisa, Denis]

Tapşırıq 2. İstifadəçilər siyahısından 3‑cü və 4‑cü unikal email-i əldə etmək

List<String> thirdAndFourthEmail = users.stream()
    .map(user -> user.email)
    .distinct()
    .skip(2)
    .limit(2)
    .collect(Collectors.toList());

System.out.println(thirdAndFourthEmail);
// Məsələn: [eva@mail.com, gleb@mail.com]

Tapşırıq 3. 10‑dan böyük olan ilk 5 unikal ədədi almaq

List<Integer> numbers = List.of(5, 12, 17, 5, 23, 17, 42, 19, 12, 8);

List<Integer> result = numbers.stream()
    .filter(n -> n > 10)
    .distinct()
    .limit(5)
    .collect(Collectors.toList());

System.out.println(result);
// Çıxış: [12, 17, 23, 42, 19]

6. Vizual sxem: əməliyyatların tətbiq sırası

graph TD
    A[İlkin siyahı] --> B[filter]
    B --> C[distinct]
    C --> D[skip]
    D --> E[limit]
    E --> F[collect]

Şərh:
Adətən əvvəlcə filter tətbiq olunur, sonra distinct ilə dublikatlar çıxarılır, daha sonra skiplimit, sonda isə — collect. Lakin tapşırıq tələb edirsə, ardıcıllığı dəyişmək olar.

7. distinct, limit, skip ilə işləyərkən tipik səhvlər

Səhv №1: distinct-in “istənilən” əlamətə görə dublikatları sildiyini gözləmək.
Əslində, distinct obyektin equals metoduna əsasən işləyir. Unikalığı hər hansı bir sahəyə (məsələn, yalnız email) görə istəyirsinizsə — ya equals/hashCode-u override etmək, ya da Collectors.toMap() və ya əlavə filtrləmə kimi fəndlərdən istifadə etmək lazımdır.

Səhv №2: Əməliyyatların ardıcıllığını pozmaq.
Əgər əvvəlcə limit, sonra distinct tətbiq etsəniz, dublikatlar qala bilər — axını ilk N elementlə məhdudlaşdırmısınız və onlar arasında eyni olanlar ola bilər.

Səhv №3: skip ilə mövcud olandan artıq elementi ötürmək.
Axında olandan çox elementi ötürməyə çalışsanız, nəticə sadəcə boş siyahı olacaq — xəta olmayacaq, amma nəticə sizi təəccübləndirə bilər.

Səhv №4: Performansı nəzərə almamaq.
distinct, limit, skip çox böyük axınlarda səmərəsiz ola bilər, xüsusən axın nizamsızdırsa və ya elementlər mürəkkəbdirsə. Adi tapşırıqların 99%-ində bu problem deyil, amma milyonlarla yazı ilə işləyirsinizsə — düşünməyə dəyər.

Səhv №5: Öz siniflər üçün equals/hashCode override edilməsini unutmaq.
Əgər istifadəçi obyektləri ilə işləyirsinizsə (məsələn, User), bu metodlar override edilmədən distinct obyektləri “məntiqən” eyni olsalar belə, fərqli hesab edəcək.

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