CodeGym /Kurslar /JAVA 25 SELF /Iterable və Iterator: kolleksiyaları dolaşmaq

Iterable və Iterator: kolleksiyaları dolaşmaq

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

1. Iterable interfeysi

Java-da demək olar ki, bütün kolleksiyalar (Map istisna olmaqla) Iterable interfeysini reallaşdırır. Bu o deməkdir ki, onlardan ardıcıl — element-addım qaydasında, daxili quruluşa varmadan keçmək olar. Proqramçı üçün bu belə görünür: “kolleksiyanın bütün elementlərdən keçmək üçün daxili üsulu var”.

Iterable interfeysi cəmi bir metodu müəyyənləşdirir:

Iterator<E> iterator();

iterator() metodu Iterator tipində obyekt qaytarır — kolleksiyadan addım-addım necə keçməyi bilən “köməkçi”. Bu sayədə vərdiş etdiyimiz for-each dövrü işləyir:

for (ElementType e : collection) {
    // ...
}

— pərdə arxasında məhz həmin Iterator gizlənir. Əlavə üstünlük: onun köməyi ilə keçid zamanı remove() metodu ilə elementləri təhlükəsiz silmək olar. Bunu adi keçidlə etməyə çalışsanız, asanlıqla ConcurrentModificationException ala bilərsiniz.

2. Iterator interfeysi

Iterator — kolleksiyanızla gedə bilən, elementləri ötürməyən və onun keçid qaydasına riayət edən “kuryer”dir.

Metod Təsvir
boolean hasNext()
Dolanmaq üçün hələ element varmı?
E next()
Növbəti elementi qaytarmaq və ona keçmək
void remove()
Cari elementi təhlükəsiz, problemsiz silmək

Kolleksiyanı Iterator ilə dolaşma nümunəsi

import java.util.*;

public class IteratorDemo {
    public static void main(String[] args) {
        List<String> tasks = new ArrayList<>();
        tasks.add("Pişiyi sığallamaq");
        tasks.add("Ev tapşırığını etmək");
        tasks.add("Seriala baxmaq");

        Iterator<String> it = tasks.iterator();
        while (it.hasNext()) {
            String task = it.next();
            System.out.println("Tapşırıq: " + task);
        }
    }
}

Burada nə baş verir?

  • tasks.iterator() vasitəsilə iterdatoru əldə edirik.
  • Növbəti element olduğu müddətcə (hasNext() true qaytaranda), onu next() ilə götürüb çıxarırıq.
  • İterator keçid qaydasını özü qoruyur — kolleksiyanın elementləri daxilində necə saxladığını bilməyiniz lazım deyil.

3. Dövr operatorları varkən nə üçün Iterator lazımdır?

Iterator sayəsində istənilən kolleksiyanı, hətta indekssiz (məsələn, Set) də dolaşmaq olar. Bu, kolleksiyanın konkret tipindən asılı olmayan universal üsuldur.

Elementlərin təhlükəsiz silinməsi

Tez-tez rast gəlinən bir vəzifə: kolleksiyadan keçmək və bəzi elementləri silmək. Bunu for-each ilə etsəniz, xəta ala bilərsiniz:

for (String task : tasks) {
    if (task.contains("pişik")) {
        tasks.remove(task); // BAM! ConcurrentModificationException
    }
}

Bu niyə baş verir? Kolleksiya, iteratordan başlayan keçid zamanı onun strukturunun birbaşa dəyişdirilməsini gözləmir.

Düzgün üsul:

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("pişik")) {
        it.remove(); // Hər şey problemsiz keçəcək!
    }
}

Niyə sadəcə indekslərdən istifadə etmək olmur?

Çünki bütün kolleksiyalarda indeks yoxdur. Məsələn, HashSet və ya TreeSet-də “beşinci element” anlayışı yoxdur. Iterator hər zaman işləyir — gücü də bundadır.

4. for-each haqqında detalları

Təkmilləşdirilmiş for dövrü (for-each) hələ Java 5-də ortaya çıxıb. Əslində bu, elementləri maksimum sadəliklə dolaşmağa imkan verən sintaktik şəkərdir:

for (String task : tasks) {
    System.out.println("Tapşırıq: " + task);
}

Kulis arxasında kompilyator iterator() çağırır, elementləri hasNext() ilə yoxlayır və next() vasitəsilə çıxarır. Sözün əsl mənasında insan dilindəki ifadə kimi oxunur: “siyahıdakı hər bir tapşırıq üçün”.

for-each nə vaxt uyğun olmur?

  • Keçid zamanı elementləri silmək lazımdır (for-each birbaşa remove() çağırmağa imkan vermir).
  • Məsələn, elementi mövqe üzrə əvəz etmək üçün indeksə çıxış lazımdır.
  • Siz Map ilə işləyirsiniz — onda “açar-dəyər” cütləri var, dolaşmaq üçün ayrıca məntiq tələb olunur.

5. Map üzrə dolaşma: incəliklər və nüanslar

Map interfeysi birbaşa Iterable-i reallaşdırmır, çünki o, “açar-dəyər” cütlərindən ibarətdir. Bununla belə, Map dolaşma üçün əlverişli təqdimatlar verir.

Açarlar üzrə dolaşma

Map<String, String> users = new HashMap<>();
users.put("vasya", "vasya@example.com");
users.put("petya", "petya@gmail.com");

for (String login : users.keySet()) {
    System.out.println("Login: " + login);
}

Dəyərlər üzrə dolaşma

for (String email : users.values()) {
    System.out.println("Email: " + email);
}

Cütlər üzrə dolaşma (açar-dəyər)

Ən universal üsul — entrySet() üzrə dolaşmadır:

for (Map.Entry<String, String> entry : users.entrySet()) {
    System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}

Maraqlı fakt: EntryMap-in daxili interfeysidir və getKey()getValue() metodlarına malikdir. Bu sayədə cütün hər iki hissəsini dərhal alırsınız.

Iterator vasitəsilə dolaşma

Iterator<Map.Entry<String, String>> it = users.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<String, String> entry = it.next();
    // Hətta elementi təhlükəsiz silmək də olar:
    if (entry.getKey().startsWith("v")) {
        it.remove();
    }
}

6. Həyatdan nümunələr: kolleksiyaları dolaşmaq tətbiqdə necə kömək edir

Nümunə: istifadəçinin bütün tapşırıqlarını çıxarırıq

List<String> tasks = new ArrayList<>();
tasks.add("Ev tapşırığını etmək");
tasks.add("Pişiyi sığallamaq");
tasks.add("Seriala baxmaq");

System.out.println("Bugünkü tapşırıqlarınız:");
for (String task : tasks) {
    System.out.println("- " + task);
}

İndi “pişik” sözünü ehtiva edən bütün tapşırıqları silək:

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("pişik")) {
        it.remove();
    }
}
System.out.println("Qalan tapşırıqlar:");
for (String task : tasks) {
    System.out.println("- " + task);
}

Nümunə: unikal loginləri Set vasitəsilə dolaşmaq

Set<String> logins = new HashSet<>();
logins.add("vasya");
logins.add("petya");
logins.add("masha");

for (String login : logins) {
    System.out.println("İstifadəçi: " + login);
}

Diqqət: Set-də çıxışın sırası istənilən ola bilər!

Nümunə: istifadəçiləri göstərmək üçün Map üzrə dolaşma

Map<String, String> users = new HashMap<>();
users.put("vasya", "vasya@example.com");
users.put("petya", "petya@gmail.com");

for (Map.Entry<String, String> entry : users.entrySet()) {
    System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}

7. Iterator.remove(): elementlərin təhlükəsiz silinməsi

Yeni başlayanların ən çox etdiyi səhvlərdən biri — for-each ilə dolaşma zamanı kolleksiya elementlərini silməyə cəhd etməkdir. İterator bu problemi remove() vasitəsilə həll edir.

Bu necə işləyir?

  • it.remove() çağırıldıqda cari element silinir — son next() tərəfindən qaytarılan element.
  • Bu, təhlükəsizdir: kolleksiya ConcurrentModificationException atmır.

Nümunə:

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    int n = it.next();
    if (n % 2 == 0) {
        it.remove(); // Bütün cüt ədədləri silirik
    }
}
System.out.println(numbers); // [1, 3, 5]

Kolleksiyanı dolaşma sxemi

+---------+     +---------+     +---------+
| Element | --> | Element | --> | Element | ...
+---------+     +---------+     +---------+
     ^               ^
     |               |
   next()         next()

İterator, hasNext() false qaytarana qədər elementlər üzrə “addımlayır”.

9. Iterator və kolleksiyaların dolaşılması ilə işləyərkən tipik səhvlər

Səhv №1: kolleksiyanı for-each ilə dolaşarkən onu modifikasiya etmək.
Elementi birbaşa for-each daxilində silməyə cəhd ConcurrentModificationException-a gətirib çıxarır:

for (String task : tasks) {
    if (task.contains("pişik")) {
        tasks.remove(task); // BAM! ConcurrentModificationException
    }
}

Iterator və onun remove()-undan istifadə edin.

Səhv №2: remove() çağırışını next()-dən əvvəl etmək.
Əvvəlcə cari elementi next() vasitəsilə almaq lazımdır, əks halda iteratorda nəyi silmək lazım olduğu aydın deyil.

Iterator<String> it = tasks.iterator();
it.remove(); // Səhv! Əvvəlcə next() lazımdır

Səhv №3: Map-i birbaşa for-each ilə dolaşmağa cəhd.
Map birbaşa Iterable-i reallaşdırmır — keySet(), values() və ya entrySet()-dən istifadə edin.

Map<String, String> users = new HashMap<>();
// for (String entry : users) { ... } // Səhv: belə olmaz
for (Map.Entry<String, String> e : users.entrySet()) {
    // düzgün
}

Səhv №4: iteratordan istifadə edərək dolaşma zamanı kolleksiyanı iteratordan kənar dəyişmək.
Keçid zamanı elementləri yalnız it.remove() ilə silin, kolleksiyanın metodları ilə deyil.

Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
    String task = it.next();
    if (task.contains("pişik")) {
        tasks.remove(task); // Səhv! it.remove() lazımdır
    }
}
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION