1. zip ilə tanışlıq
Proqramlaşdırmada zip termini (və ya “zipləmə”, “birləşdirmə”) iki (və ya daha çox) siyahını götürüb onları bir cütlər axınına birləşdirən əməliyyatı ifadə edir: hər siyahıdan bir element olmaqla. Əgər Python ilə tanışsınızsa, orada məhz bunu edən zip funksiyası var.
Nümunə:
- Adlar siyahısı: ["Anya", "Boris", "Vika"]
- Yaşlar siyahısı: [20, 25, 19]
- “Zipləmə”dən sonra alınacaq: [("Anya", 20), ("Boris", 25), ("Vika", 19)]
Bu, iki kolleksiyanı sinxron emal etmək lazım olanda rahatdır — məsələn, ad və yaşın birlikdə getdiyi obyektlər yaratmaq üçün.
Niyə Stream API-də zip yoxdur?
Standart Stream API-də (Java 22-yə qədər) zip metodu yoxdur. Səbəb odur ki, axınlar sonsuz ola bilər, kolleksiyaların uzunluqları fərqli ola bilər və bir kolleksiya digərindən uzun olduqda necə davranmalı olduğun həmişə aydın deyil. Amma praktikada zip tez-tez lazım olur.
2. Java-da zip-in reallaşdırılması: daxili metod olmadan necə yaşamaq
Ən sadə üsul — indekslər vasitəsilə
Əgər sizdə iki siyahı varsa və bunun “adi” kolleksiyalar olduğuna əminsinizsə (məsələn, List<A>, List<B>), indekslərdən istifadə etmək olar:
import java.util.*;
import java.util.stream.*;
public class ZipExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Anya", "Boris", "Vika");
List<Integer> ages = Arrays.asList(20, 25, 19);
int size = Math.min(names.size(), ages.size());
List<Person> people = IntStream.range(0, size)
.mapToObj(i -> new Person(names.get(i), ages.get(i)))
.collect(Collectors.toList());
people.forEach(System.out::println);
}
static class Person {
String name;
int age;
Person(String name, int age) { this.name = name; this.age = age; }
public String toString() { return name + " (" + age + ")"; }
}
}
Çıxış:
Anya (20)
Boris (25)
Vika (19)
Nə baş verir?
- İki siyahının minimal ölçüsünü götürürük — sərhəddən kənara çıxmamaq üçün.
- IntStream.range(0, size) istifadə edirik — indekslər axını yaradırıq.
- Hər indeks üçün hər iki siyahıdan bir element götürüb onları “birləşdiririk”.
- Nəticəni Collectors.toList() ilə siyahıya toplayırıq.
Stream<T> üçün zip etmək olar?
Texniki cəhətdən — bəli, amma bu, yalnız hər iki axın sonlu və arxasında indekslə sürətli giriş imkanı olan struktur (yəni, əslində elə List) olduqda rahatdır. “Həqiqi” axınlar (məsələn, sonsuz) üçün korrekt zip reallaşdırması daha çətindir və əlavə məntiq tələb edir.
Alternativ variantlar: üçüncü tərəf kitabxanaları
“Hazır” zip istəyirsinizsə, aşağıdakı kitabxanalardan istifadə edə bilərsiniz:
- org.apache.commons.lang3.Streams.zip (Apache Commons Lang 3.10+)
- io.vavr.collection.Stream.zip (Vavr)
- com.codepoetics.protonpack.StreamUtils.zip (ProtonPack)
Bu kursda standart kitabxanadan istifadə edirik, buna görə də “əl ilə” üsulları nəzərdən keçiririk.
3. zip istifadəsinin praktiki nümunələri
Nümunə 1. İki kolleksiyanın sinxron keçidi (elementlərin cəmlənməsi)
List<Integer> a = Arrays.asList(1, 2, 3, 4);
List<Integer> b = Arrays.asList(10, 20, 30, 40);
List<Integer> sums = IntStream.range(0, Math.min(a.size(), b.size()))
.mapToObj(i -> a.get(i) + b.get(i))
.collect(Collectors.toList());
System.out.println(sums); // [11, 22, 33, 44]
Nümunə 2. Sətirlərin və simvolların ziplənməsi
String[] words = {"cat", "dog", "fox"};
char[] marks = {'!', '?', '.'};
List<String> zipped = IntStream.range(0, Math.min(words.length, marks.length))
.mapToObj(i -> words[i] + marks[i])
.collect(Collectors.toList());
System.out.println(zipped); // [cat!, dog?, fox.]
Vizualizasiya (sxem)
names: [Anya] [Boris] [Vika]
ages: [20 ] [25 ] [19 ]
| | |
zip ---> (Anya,20) (Boris,25) (Vika,19)
4. Stream.iterate və Stream.generate — yeni axınların yaradılması
Bəzən təkcə mövcud kolleksiyaları emal etmək deyil, həm də “yerindəcə” yeni ardıcıllıqlar yaratmaq lazımdır. Bunun üçün Stream API-də iki faydalı metod var:
- Stream.iterate — qaydaya əsasən ardıcıllıq yaradır (məsələn, arifmetik proqressiya).
- Stream.generate — hər elementin Supplier vasitəsilə hesablandığı axın yaradır (məsələn, təsadüfi ədəd, cari vaxt və s.).
Stream.iterate
Sintaksis:
Stream.iterate(seed, unaryOperator)
- seed — başlanğıc dəyər;
- unaryOperator — növbəti elementi hesablayan funksiya.
Nümunə 1: Arifmetik proqressiya
Stream<Integer> numbers = Stream.iterate(0, n -> n + 2); // 0, 2, 4, 6, ...
numbers.limit(5).forEach(System.out::println);
// Çap edəcək: 0 2 4 6 8
Nümunə 2: Tarixlərin generasiyası
import java.time.LocalDate;
Stream<LocalDate> days = Stream.iterate(LocalDate.now(), date -> date.plusDays(1));
days.limit(3).forEach(System.out::println);
// Məsələn: 2024-06-09, 2024-06-10, 2024-06-11
Nümunə 3: Sonsuz axın — limit etməyi unutmayın!
Stream<Integer> endless = Stream.iterate(1, n -> n * 2);
endless.limit(5).forEach(System.out::println); // 1 2 4 8 16
Java 9+ versiyasında şərt predikatı ilə aşırı yüklənmiş variant mövcuddur:
Stream.iterate(0, n -> n < 10, n -> n + 2)
.forEach(System.out::println); // 0 2 4 6 8
Stream.generate
Sintaksis:
Stream.generate(Supplier<T>)
Hər element Supplier.get() çağırışı ilə hesablanır.
Nümunə 1: Təsadüfi ədədlər
import java.util.Random;
Random random = new Random();
Stream<Integer> randoms = Stream.generate(random::nextInt);
randoms.limit(5).forEach(System.out::println);
Nümunə 2: Eyni dəyərlərin generasiyası
Stream<String> stars = Stream.generate(() -> "*");
stars.limit(4).forEach(System.out::print); // ****
Nümunə 3: Unikal identifikatorlar
import java.util.UUID;
Stream<String> uuids = Stream.generate(() -> UUID.randomUUID().toString());
uuids.limit(3).forEach(System.out::println);
Vizualizasiya (sxem)
Stream.iterate:
[seed] -> op() -> op() -> op() -> ...
n n+1 n+2 n+3
Stream.generate:
Supplier() -> Supplier() -> Supplier() -> ...
val1 val2 val3
5. İstifadə nümunələri: tətbiq üçün məlumatların generasiyası
Tutaq ki, Student sinfimiz var:
class Student {
String name;
int age;
Student(String name, int age) { this.name = name; this.age = age; }
public String toString() { return name + " (" + age + ")"; }
}
Nümunə 1. Test tələbələrinin generasiyası
List<String> names = Arrays.asList("Anya", "Boris", "Vika", "Gleb", "Dasha");
Stream<Student> students = IntStream.range(0, names.size())
.mapToObj(i -> new Student(names.get(i), 18 + i));
students.forEach(System.out::println);
// Anya (18), Boris (19), Vika (20), Gleb (21), Dasha (22)
Nümunə 2. Təsadüfi tələbələrin generasiyası
Random random = new Random();
List<String> pool = Arrays.asList("Ira", "Oleg", "Maksim", "Tanya", "Sergey");
Stream<Student> randomStudents = Stream.generate(() ->
new Student(
pool.get(random.nextInt(pool.size())),
18 + random.nextInt(5)
)
);
randomStudents.limit(3).forEach(System.out::println);
// Məsələn: Tanya (19), Oleg (21), Ira (20)
Nümunə 3. Hesabat üçün tarix ardıcıllığının generasiyası
import java.time.LocalDate;
Stream<LocalDate> dates = Stream.iterate(LocalDate.of(2024, 6, 1), d -> d.plusDays(1));
dates.limit(5).forEach(System.out::println);
// 2024-06-01, 2024-06-02, ..., 2024-06-05
Müqayisə: zip, iterate, generate nə zaman istifadə olunmalıdır
- zip — iki (və ya daha çox) siyahı/axını indekslər üzrə birləşdirib sinxron emal etmək lazım olanda.
- iterate — qaydaya əsasən ardıcıllıq lazım olanda (rəqəmlər, tarixlər, addımlar).
- generate — hər bir element müstəqil hesablandıqda (təsadüfi dəyərlər, unikal ID-lər).
7. zip və axınların generasiyası ilə işləyərkən tipik xətalar
Xəta № 1: limit olmadan qeyri-məhdud axın. Əgər Stream.iterate və ya Stream.generate-dən limit ilə məhdudlaşdırmadan istifadə etsəniz, proqram ilişə bilər və ya bütün yaddaşı “yeyə” bilər.
Stream.generate(() -> 1).forEach(System.out::println); // Heç vaxt bitməyəcək!
Xəta № 2: zip zamanı fərqli uzunluqların yanlış emalı. Əgər bir siyahı digərindən uzundursa, minimal uzunluğa qədər getmək lazımdır, əks halda IndexOutOfBoundsException alacaqsınız.
Xəta № 3: adi Stream<T> üzərində zip etməyə cəhd. Adi axınlarda indekslə giriş yoxdur. Praktik zip daha çox List üçün edilir.
Xəta № 4: Axın yaradılarkən kolleksiyanı dəyişdirmək. Əgər axın gedərkən kolleksiyanı dəyişsəniz, ConcurrentModificationException ala bilərsiniz. Yeni məlumatlar yaradın — köhnəni “yolda” dəyişməyin.
Xəta № 5: Sıralamanın itməsi. Sıra önəmlidirsə (məsələn, zip zamanı), List istifadə edin, Set yox — əks halda elementlərin sırası qeyri-müəyyən olacaq.
GO TO FULL VERSION