1. Union əməliyyatı (çoxluqların birləşdirilməsi)
Java-da çoxluqları təqdim etmək üçün Set<T> interfeysindən istifadə olunur. Siyahılardan (List) fərqli olaraq, çoxluqlar elementlərin unikal olmasını təmin edir və, bir qayda olaraq, düzülüş qaydası ilə maraqlanmır (yalnız xüsusi realizasiyalar, məsələn, LinkedHashSet istifadə olunmadıqda). Ən populyar realizasiyalar — HashSet və TreeSet. Onların əsas vəzifəsi — elementin mövcudluğunu sürətlə müəyyənləşdirmək və dublikatların qarşısını almaqdır.
Çoxluqlar nə vaxt lazımdır?
- Unikallıq vacib olduqda: məsələn, saytı ziyarət etmiş bütün unikal istifadəçilərin siyahısı.
- Elementin mövcudluğunu tez yoxlamaq lazım olduqda: HashSet-də contains metodu adətən sabit zamanda işləyir.
- Çoxluqlar üzərində tipik əməliyyatları icra etmək lazım olduqda: birləşdirmə, kəsişmə, fərq.
Union — iki və daha çox çoxluğun birləşməsidir: nəticə hər iki mənbə çoxluğunun bütün elementlərini (təkrarsız) ehtiva edir.
Praktik nümunə
Fərz edək ki, «Robotexnika» və «Proqramlaşdırma» dərnəklərinə gələn iki tələbə çoxluğumuz var:
Set<String> robotics = Set.of("Anya", "Boris", "Vika");
Set<String> programming = Set.of("Vika", "Gleb", "Dasha");
Biz ən azı bir dərnəyə gedən bütün tələbələrin çoxluğunu əldə etməliyik.
Stream API ilə həll
Ən birbaşa yol — hər iki stream-i birləşdirib onları Set-ə toplamaqdır:
Set<String> all = Stream.concat(
robotics.stream(),
programming.stream()
).collect(Collectors.toSet());
System.out.println(all); // [Anya, Boris, Vika, Gleb, Dasha]
İzah:
- Stream.concat iki stream-i birləşdirir.
- collect(Collectors.toSet()) elementləri çoxluğa yığır (dublikatları avtomatik çıxarır).
Alternativ: ikidən çox çoxluq
Əgər üç və daha çox dərnək varsa, Stream.of və flatMap istifadə edin:
Set<String> math = Set.of("Zhenya", "Vika", "Boris");
Set<String> all = Stream.of(robotics, programming, math)
.flatMap(Set::stream)
.collect(Collectors.toSet());
System.out.println(all); // [Anya, Boris, Vika, Gleb, Dasha, Zhenya]
Niyə məhz Set?
Çünki Set dublikatları avtomatik çıxarır. Əgər List-ə toplasanız, eyni adlar bir neçə dəfə rast gəlinəcək.
2. Intersection (çoxluqların kəsişməsi) əməliyyatı
Intersection — eyni anda hər iki çoxluqda olan elementlərdir.
Praktik nümunə
Həm «Robotexnika», həm də «Proqramlaşdırma»ya gedən tələbələri tapmaq:
Set<String> robotics = Set.of("Anya", "Boris", "Vika");
Set<String> programming = Set.of("Vika", "Gleb", "Dasha");
Stream API ilə həll:
Set<String> both = robotics.stream()
.filter(programming::contains)
.collect(Collectors.toSet());
System.out.println(both); // [Vika]
İzah:
«Robotexnika» iştirakçılarını keçib yalnız «Proqramlaşdırma»da da olanları filtr edirik. Nəticə — hər iki dərnəkdə olan adların çoxluğu.
Alternativ üsul (Stream API olmadan)
Daxili retainAll metodundan istifadə etmək olar (o, cari çoxluğu dəyişir):
Set<String> intersection = new HashSet<>(robotics);
intersection.retainAll(programming);
System.out.println(intersection); // [Vika]
Ancaq mövzu Stream API olduğuna görə diqqəti stream-lərə yönəldək.
3. Difference (çoxluqların fərqi) əməliyyatı
Difference — birinci çoxluqda olub ikinci çoxluqda olmayan elementlər.
Praktik nümunə
Yalnız «Robotexnika»ya gedib «Proqramlaşdırma»ya getməyən tələbələri tapmaq:
Set<String> robotics = Set.of("Anya", "Boris", "Vika");
Set<String> programming = Set.of("Vika", "Gleb", "Dasha");
Stream API ilə həll:
Set<String> onlyRobotics = robotics.stream()
.filter(name -> !programming.contains(name))
.collect(Collectors.toSet());
System.out.println(onlyRobotics); // [Anya, Boris]
İzah:
«Robotexnika» iştirakçılarını filtr edirik, «Proqramlaşdırma»da olmayanları saxlayırıq.
Alternativ üsul (Stream API olmadan)
Set<String> difference = new HashSet<>(robotics);
difference.removeAll(programming);
System.out.println(difference); // [Anya, Boris]
4. Praktiki tapşırıqlar: tələbə siyahılarının emalı
Tapşırıq 1: Yalnız bir dərnəyə gedən tələbələri tapın
Yalnız «Robotexnika»ya və ya yalnız «Proqramlaşdırma»ya gedən, lakin hər ikisinə birgə yox. Bu, simmetrik fərqdir (xor çoxluqlar üçün):
Set<String> onlyOne = Stream.concat(
robotics.stream().filter(name -> !programming.contains(name)),
programming.stream().filter(name -> !robotics.contains(name))
).collect(Collectors.toSet());
System.out.println(onlyOne); // [Anya, Boris, Gleb, Dasha]
Tapşırıq 2: Bir neçə dərnəkdən bütün unikal tələbələrin siyahısı
Set<String> all = Stream.of(robotics, programming, math)
.flatMap(Set::stream)
.collect(Collectors.toSet());
System.out.println(all); // [Anya, Boris, Vika, Gleb, Dasha, Zhenya]
Tapşırıq 3: Heç bir dərnəyə getməyən tələbələri tapın
Fərz edək, bizdə sinifdəki bütün tələbələrin siyahısı var:
Set<String> allStudents = Set.of("Anya", "Boris", "Vika", "Gleb", "Dasha", "Zhenya", "Igor’", "Katya");
Heç bir dərnəyə getməyənləri tapmaq lazımdır:
Set<String> attendees = Stream.of(robotics, programming, math)
.flatMap(Set::stream)
.collect(Collectors.toSet());
Set<String> notInAny = allStudents.stream()
.filter(name -> !attendees.contains(name))
.collect(Collectors.toSet());
System.out.println(notInAny); // [Igor’, Katya]
5. Vacib qeydlər: equals, hashCode və performans
Niyə equals və hashCode metodlarını düzgün reallaşdırmaq vacibdir?
Çoxluqlarla (Set) bütün əməliyyatlar equals və hashCode metodlarının düzgünlüyündən asılıdır. Əgər öz sinfinizin obyektlərini (məsələn, Student) saxlayırsınızsa, bu metodları mütləq yenidən müəyyənləşdirin, əks halda müqayisələr düzgün işləməyəcək.
Nümunə:
class Student {
String name;
int age;
// equals və hashCode-u yenidən müəyyən etməyi unutmayın!
}
Bunu etməsəniz, eyni ad və yaşa malik iki tələbə Set üçün fərqli obyektlər sayılacaq.
Niyə List deyil, Set istifadə etmək daha yaxşıdır?
- Set-də contains əməliyyatı sürətli işləyir (adətən sabit zamanda).
- List-də elementin axtarışı xətti vaxt aparır ki, bu da böyük kolleksiyalar üçün kritik ola bilər.
- Çoxluqlar üzərindəki əməliyyatlar üçün (union, intersection, difference) Set daha səmərəli və məntiqlidir.
6. Çoxluqlarla əməliyyatlarda tipik səhvlər
Səhv №1: Çoxluq əməliyyatları üçün Set əvəzinə List istifadə etmək. Əgər elementləri List-ə yığırsınızsa, dublikatlar silinmir və contains əməliyyatı yavaşdır. union/intersection/difference üçün Set istifadə edin.
Səhv №2: Obyektlər üçün equals/hashCode reallaşdırılmayıb. Əgər Set-də öz sinfinizin obyektlərini saxlayırsınızsa və equals və hashCode metodlarını override etməmisinizsə, kəsişmə və fərq «qəribə» işləyəcək — məna etibarilə eyni olan obyektlər bərabər sayılmayacaq.
Səhv №3: Stream zamanı kolleksiyanı modifikasiya etmək. Əgər birbaşa stream daxilində ilkin Set-i dəyişməyə (məsələn, element əlavə etməyə və ya silməyə) çalışsanız, ConcurrentModificationException alacaqsınız. Həmişə yeni çoxluqla işləyin.
Səhv №4: Sıralamanın qeyri-aşkar itirilməsi. HashSet elementlərin sırasını təmin etmir. Sıra vacibdirsə — LinkedHashSet və ya TreeSet istifadə edin.
Səhv №5: İkidən çox kolleksiya üçün Stream.concat istifadə etmək. Stream.concat yalnız iki stream-i birləşdirir. Daha çoxu üçün Stream.of(...) və flatMap istifadə edin.
Səhv №6: null ilə bağlı səhvlər. Çoxluqlar null-dəyərləri xoşlamır, xüsusilə Set.of(...) istifadə edirsinizsə — o, null-a icazə vermir. null ilə işləmək üçün digər realizasiyalardan istifadə edin və ya dəyərləri əvvəlcədən filtr edin.
GO TO FULL VERSION