CodeGym /Kurslar /JAVA 25 SELF /Stream-lərlə işləyərkən tipik səhvlərin təhlili

Stream-lərlə işləyərkən tipik səhvlərin təhlili

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

1. Səhv: stream-dən təkrar istifadə

Java-da Stream — birdəfəlik alətdir. Terminal əməliyyatı icra edən kimi (məsələn, collect(), forEach(), count()) stream bağlanır və daha istifadə üçün yararsız olur.

Səhv nümunəsi

Stream<String> stream = Stream.of("a", "b", "c");
stream.forEach(System.out::println); // Hər şey qaydasındadır
stream.forEach(System.out::println); // IllegalStateException atacaq!

Çıxış:

a
b
c
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed

Necə qarşısını almaq olar

Bir neçə əməliyyat etmək lazımdırsa — hər dəfə yeni stream yaradın:

List<String> list = List.of("a", "b", "c");
list.stream().forEach(System.out::println); // OK
list.stream().count(); // OK

Lifehack: Stream yeməkxananın birdəfəlik stəkanı kimidir: istifadə et — at, növbəti içki üçün yenisini götür.

2. Səhv: stream zamanı kolleksiyanı dəyişmək

Stream vasitəsilə keçid zamanı ilkin kolleksiyanı modifikasiya etsəniz, ConcurrentModificationException alacaqsınız. Bu, xüsusilə filtrasiya və elementlərin silinməsi zamanı tez-tez rast gəlinir.

Səhv nümunəsi

List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie"));
names.stream().forEach(name -> {
    if (name.startsWith("A")) {
        names.remove(name); // BOOM!
    }
});

Nəticə:

Exception in thread "main" java.util.ConcurrentModificationException

Necə düzgün

Keçid zamanı kolleksiyanı dəyişməyən metodlardan istifadə edin və ya nəticəni yeni kolleksiyada toplayın:

names.removeIf(name -> name.startsWith("A")); // OK, removeIf təhlükəsizdir

// Yaxud Stream API ilə:
List<String> filtered = names.stream()
    .filter(name -> !name.startsWith("A"))
    .collect(Collectors.toList());

Yadda saxlayın: Stream-lər təmizlik sevir — stream-lər onu “yeyərkən” ilkin kolleksiyaya toxunmayın.

3. Səhv: sonsuz stream-lərdə unudulmuş limit

Stream.iterate və Stream.generate metodları sonsuz stream yarada bilər. Onları limit metodu ilə məhdudlaşdırmağı unutsanız, proqram sonsuzadək asıla bilər (və ya OutOfMemoryError-a qədər).

Səhv nümunəsi

Stream<Integer> infinite = Stream.iterate(0, n -> n + 1);
infinite.forEach(System.out::println); // Proqram heç vaxt başa çatmayacaq!

Necə düzgün

Stream-i limit metodu ilə məhdudlaşdırın:

Stream<Integer> finite = Stream.iterate(0, n -> n + 1).limit(10);
finite.forEach(System.out::println); // 0-dan 9-a qədər ədədləri çap edəcək

Məsləhət: Sonsuz stream-lər final seriyası olmayan serialı xatırladır: dayanmağı kimsə etməlidir və bunu ən yaxşısı siz edin!

4. Səhv: null ilə səhv işləmə

Stream API null-u “sevmir”. Kolleksiyada null olarsa, bir çox əməliyyatlar xüsusilə elementlər üzərində metod çağırarkən NullPointerException ata bilər.

Səhv nümunəsi

List<String> names = Arrays.asList("Alice", null, "Bob");
names.stream()
    .map(String::toUpperCase) // BOOM! null-da
    .forEach(System.out::println);

Nəticə:

Exception in thread "main" java.lang.NullPointerException

Necə düzgün

Null dəyərləri əvvəlcədən süzün:

names.stream()
    .filter(Objects::nonNull)
    .map(String::toUpperCase)
    .forEach(System.out::println);

Optional ilə iş

Əgər sizdə Stream<Optional<T>> varsa, “açmaq” üçün flatMap(Optional::stream) (Java 9+) istifadə edin:

List<Optional<String>> optionals = List.of(Optional.of("A"), Optional.empty(), Optional.of("B"));
optionals.stream()
    .flatMap(Optional::stream)
    .forEach(System.out::println); // Yalnız "A" və "B"

Məsləhət: Kolleksiyalara null əlavə etməməyə çalışın. Əgər onsuz olmursa, onları başlanğıcda süzün.

5. Səhv: elementlərin sırasının itirilməsi

Bəzi əməliyyatlar (məsələn, flatMap, concat) elementlərin sırasına təsir göstərə bilər, xüsusilə paralel stream-lərlə işləyərkən və ya müxtəlif kolleksiyaları birləşdirərkən.

Səhv nümunəsi

List<Integer> list1 = List.of(1, 2, 3);
List<Integer> list2 = List.of(4, 5, 6);
Stream<Integer> combined = Stream.concat(list1.stream(), list2.stream());
combined.forEach(System.out::print); // 123456 — sıra qorunur

// Amma paralel stream-lərdən istifadə etsəniz:
Stream<Integer> par = Stream.concat(list1.parallelStream(), list2.parallelStream());
par.forEach(System.out::print); // Sıra gözlənilməz ola bilər!

Necə düzgün

Əgər sıra vacibdirsə — paralel stream-lərdən qaçın və ya sıralamaya zəmanət verən metodlardan istifadə edin (forEachOrdered):

par.forEachOrdered(System.out::print);

Fakt: Stream API susmaya görə sıranı qoruyur, yalnız siz onu “pozmasanız”.

6. Səhv: kolleksiyaların qeyri-səmərəli birləşdirilməsi

Çoxluq əməliyyatları üçün List əvəzinə Set istifadə etmək daha yaxşıdır ki, dublikatlardan qaçasınız və məhsuldarlığı artırasınız. Həmçinin flatMap-dən istifadə etmək mümkün olan yerdə iç-içə dövrlər yazmaq qeyri-səmərəlidir.

Səhv nümunəsi

List<String> list1 = List.of("a", "b", "c");
List<String> list2 = List.of("b", "c", "d");

// Qeyri-səmərəli:
List<String> union = new ArrayList<>(list1);
for (String s : list2) {
    if (!union.contains(s)) { // List-də contains — O(n)
        union.add(s);
    }
}

// Set və Stream API ilə səmərəli:
Set<String> unionSet = Stream.concat(list1.stream(), list2.stream())
    .collect(Collectors.toSet());

Yaxud fərq üçün:

// Qeyri-səmərəli:
List<String> diff = new ArrayList<>(list1);
diff.removeAll(list2); // OK, amma bunu stream və list.contains ilə etsəniz — yavaş olardı

// Səmərəli:
Set<String> set2 = new HashSet<>(list2);
List<String> difference = list1.stream()
    .filter(s -> !set2.contains(s))
    .collect(Collectors.toList());

Məsləhət: Unikal elementlərlə işləmək üçün həmişə Set istifadə edin. Böyük kolleksiyalar üçün — iç-içə dövrlərdən qaçın, stream + flatMap-ə üstünlük verin.

7. Səhv: terminal əməliyyatlarından təkrar istifadə

Bəzən stream-i iki qola “bölmək” istəyirsiniz: məsələn, elementləri siyahıda toplamaq və paralel olaraq onların sayını hesablamaq. Amma stream “birdəfəlikdir” və ikinci əməliyyat səhvlə nəticələnəcək.

Səhv nümunəsi

Stream<String> stream = Stream.of("a", "b", "c");
List<String> list = stream.collect(Collectors.toList());
long count = stream.count(); // BOOM! IllegalStateException

Necə düzgün

Stream-i bir neçə dəfə istifadə etmək lazımdırsa — əvvəlcə onu kolleksiyada toplayın:

List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
long count = list.size();

Məsləhət: Hər iki nəticə lazımdırsa — əvvəlcə kolleksiyada toplayın.

8. Səhv: resursları close() etməyi unutmaq

Resurslarla bağlı stream-lərdən istifadə edirsinizsə (məsələn, Files.lines(Path)), istifadədən sonra onları bağlamağı unutmayın ki, sızmalar olmasın.

Səhv nümunəsi

Stream<String> lines = Files.lines(Path.of("file.txt"));
lines.forEach(System.out::println); // OK, amma resurs bağlanmayıb!

Necə düzgün

Try-with-resources istifadə edin:

try (Stream<String> lines = Files.lines(Path.of("file.txt"))) {
    lines.forEach(System.out::println);
}

Məsləhət: Fayl stream-ləri çaydan kimidir: qaynat — söndür!

9. Səhv: flatMap-də Optional və null-un səhv işlənməsi

List<Optional<T>> tipli kolleksiyalarla işləyərkən bir çoxları map(Optional::get) istifadə edir, bu isə Optional boş olduqda istisnalara gətirib çıxarır.

Səhv nümunəsi

List<Optional<String>> optionals = List.of(Optional.of("A"), Optional.empty());
optionals.stream()
    .map(Optional::get) // BOOM! NoSuchElementException
    .forEach(System.out::println);

Necə düzgün

flatMap(Optional::stream) (Java 9+) istifadə edin:

optionals.stream()
    .flatMap(Optional::stream)
    .forEach(System.out::println); // Yalnız "A"

Məsləhət: Optional sadəcə “qablaşdırma” deyil, dəyərin olmaması ilə təhlükəsiz işləmək üçün alətdir.

10. Səhv: equals/hashCode-un səhv reallaşdırılması

Çoxluqlar üzərində əməliyyatlar (Set, distinct, birləşdirmə, kəsişmə) equals və hashCode metodlarının düzgün reallaşdırılmasından asılıdır.

Səhv nümunəsi

class Student {
    String name;
    int age;
    // equals və hashCode yoxdur!
}

Set<Student> students = new HashSet<>();
students.add(new Student("Alice", 20));
students.add(new Student("Alice", 20));
System.out.println(students.size()); // 2, halbuki onlar eyni olmalıydı!

Necə düzgün

Əgər siniflərinizi Set, Map kolleksiyalarında və ya distinct əməliyyatları üçün istifadə etməyi planlaşdırırsınızsa, equals və hashCode metodlarını mütləq override edin.

11. Səhv: collect-dən sonra stream-dən istifadə

Terminal əməliyyatından (məsələn, collect) sonra stream bağlanır. Eyni stream ilə işi davam etdirmək cəhdi səhvlə nəticələnəcək.

Səhv nümunəsi

Stream<String> stream = Stream.of("a", "b", "c");
List<String> list = stream.collect(Collectors.toList());
stream.filter(s -> s.startsWith("a")).forEach(System.out::println); // Exception!

Necə düzgün

Kolleksiyadan yeni stream yaradın:

list.stream().filter(s -> s.startsWith("a")).forEach(System.out::println);

12. Səhv: parallelStream-in düzgün istifadə olunmaması

Bir çoxları düşünür ki, parallelStream() həmişə sürətləndirir. Əslində, kiçik kolleksiyalar və ya sadə əməliyyatlar üçün paralellik thread idarəetmə xərclərinə görə icranı ləngidə bilər.

Səhv nümunəsi

List<Integer> numbers = IntStream.range(0, 10).boxed().collect(Collectors.toList());
numbers.parallelStream().forEach(System.out::println); // Adi stream()‑dən daha yavaş ola bilər

Necə düzgün

parallelStream-dən yalnız həqiqətən böyük kolleksiyalar və ya ağır hesablamalar üçün istifadə edin, xüsusən əməliyyatlar saf (yan təsirsiz) və assosiativ olduqda.

13. Səhv: unudulmuş terminal operatoru

Aralıq əməliyyatlar (map, filter, flatMap və s.) terminal operatoru (collect, forEach, count və s.) çağrılmayana qədər icra olunmur.

Səhv nümunəsi

Stream<String> stream = Stream.of("a", "b", "c").map(String::toUpperCase);
// Heç nə baş verməyəcək!

Necə düzgün

Mütləq terminal operatorunu çağırın:

stream.forEach(System.out::println);
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Məlumat bazasının təhlükəli təmizlənməsi və mümkün "boş" qeydlər 😼
Məlumat bazasının təhlükəli təmizlənməsi və mümkün "boş" qeydlər 😼
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Tək istifadə olunan sehrli rulo 📜
Tək istifadə olunan sehrli rulo 📜
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Birləşdirmələr və proyeksiyalar
Stream API: birləşdirmələr və proyeksiyalar
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION