Eğitim Portalı/Java/Iteratorlar (Iterators)
Java01-java/42-iteratorlar

Iteratorlar (Iterators)

Bir koleksiyonun elemanlarını tek tek gezmek, programlamanın en sık yaptığı işlerden biridir. Ama farklı koleksiyonların (liste, küme, kuyruk...) iç yapıları çok farklıdır. Iterator, bu farkı gizleyen ortak bir gezint…

Iteratorlar (Iterators)

Bir koleksiyonun elemanlarını tek tek gezmek, programlamanın en sık yaptığı işlerden biridir. Ama farklı koleksiyonların (liste, küme, kuyruk...) iç yapıları çok farklıdır. Iterator, bu farkı gizleyen ortak bir gezinti arayüzüdür: koleksiyonun tipinden bağımsız, standart bir "sıradaki elemanı ver" mekanizması. Aslında her gün kullandığın for-each döngüsü de perde arkasında Iterator'a dayanır.

Iterator: standart gezinti

Iterator üç metot sunar: hasNext() (daha eleman var mı?), next() (sıradakini ver) ve isteğe bağlı remove() (son döneni sil):

Iterator<String> it = liste.iterator();
while (it.hasNext()) {
    String e = it.next();
    ...
}

for (String e : liste) yazdığında derleyici bunu tam olarak bu Iterator döngüsüne çevirir. Örnek 1 (./Ornek1.java) temel gezintiyi ve kritik bir konuyu gösterir: gezerken güvenli silme.

fail-fast ve gezerken değiştirme

Bir koleksiyonu for-each ile gezerken aynı anda liste.remove(...) çağırırsan ConcurrentModificationException alırsın. Bu "fail-fast" davranıştır: koleksiyon, gezinti sırasında yapısal olarak değiştiğini fark eder ve hemen hata verir (sessiz, bozuk sonuç yerine).

Doğru yollar:

  • Gezerken silmek için iterator.remove() kullan (güvenli).
  • Daha temizi: koşullu silme için removeIf(predicate) (iç tarafta iterator kullanır).
it.remove();                       // güvenli, gezinti sırasında
liste.removeIf(s -> s.isEmpty());  // en temiz

ListIterator: çift yönlü ve güçlü

List'ler ayrıca ListIterator sunar: ileri ve geri gidebilir, gezerken güncelleyebilir (set) ve ekleyebilir (add), konumu sorgulayabilir (nextIndex/previousIndex):

ListIterator<Integer> it = list.listIterator();
while (it.hasNext()) { int x = it.next(); it.set(x * x); } // yerinde güncelle
while (it.hasPrevious()) { ... }                            // geri git

Örnek 2 (./Ornek2.java) bir listeyi set ile yerinde güncellemeyi, geri gezintiyi ve add ile ekleme yapmayı gösterir.

Kendi Iterable'ını yazmak

Kendi sınıfını for-each ile gezilebilir yapmak istiyorsan Iterable<T> uygula: tek metodu iterator()'dır ve bir Iterator<T> döndürür:

class Aralik implements Iterable<Integer> {
    public Iterator<Integer> iterator() {
        return new Iterator<>() {
            public boolean hasNext() { ... }
            public Integer next() { ... }
        };
    }
}
for (int s : new Aralik(1, 5)) { ... }   // artık for-each çalışır

Örnek 3 (./Ornek3.java) 1..n aralığını for-each ile gezilebilir bir Iterable olarak yazar. Bu, kendi veri yapılarını (ağaç, grafik, sayfalı akış...) dilin doğal döngüsüne bağlamanı sağlar.

Iterator vs Enumeration

Iterator, eski Enumeration'ın (Java 1.0) halefidir; daha kısa metot adları ve remove() desteği getirir. Eski API'lerde (Vector, Hashtable) hâlâ Enumeration görürsün; onu ayrı bir konuda ele alıyoruz.

Özet

Koleksiyonları tipinden bağımsız gezmenin standart yolu olan Iterator'ı (Örnek 1); fail-fast davranışını ve gezerken güvenli silmeyi (iterator.remove/removeIf); çift yönlü ve güncelleyebilen ListIterator'ı (Örnek 2) ve kendi sınıfını for-each'e açan Iterable'ı (Örnek 3) öğrendik. Sırada, nesneleri sıralamanın temeli — bir nesnenin "doğal sırasını" tanımlamak: Comparable arayüzü.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Iterator — bir koleksiyonu güvenle gezmek ve gezerken eleman silmek.
2// Çalıştırma: java Ornek1.java
3import java.util.ArrayList;
4import java.util.Iterator;
5import java.util.List;
6
7public class Ornek1 {
8
9    public static void main(String[] args) {
10        List<String> liste = new ArrayList<>(List.of("elma", "armut", "", "kiraz", "", "muz"));
11
12        // Iterator: hasNext() -> next() döngüsü. for-each'in arkasındaki mekanizma budur.
13        Iterator<String> it = liste.iterator();
14        System.out.print("Gezinti: ");
15        while (it.hasNext()) {
16            System.out.print(it.next() + " ");
17        }
18        System.out.println();
19
20        // GEZERKEN SİLME: for-each içinde liste.remove(...) -> ConcurrentModificationException!
21        // Doğrusu: iterator.remove() ile güvenli silme.
22        Iterator<String> it2 = liste.iterator();
23        while (it2.hasNext()) {
24            if (it2.next().isEmpty()) {
25                it2.remove();   // güvenli: iterator üzerinden sil
26            }
27        }
28        System.out.println("Boşlar silindikten sonra: " + liste);
29
30        // Aynı işi modern yol: removeIf (iç tarafta iterator kullanır)
31        liste.removeIf(s -> s.length() > 4);
32        System.out.println("4+ harfliler silindikten sonra: " + liste);
33
34        System.out.println("""
35
36                --- Iterator ---
37                Iterator, bir koleksiyonu tipinden bağımsız gezmenin standart yoludur (hasNext/next/remove).
38                for-each döngüsü aslında arka planda Iterator kullanır.
39                Gezerken eleman silmek için MUTLAKA iterator.remove() kullan; koleksiyonu doğrudan
40                değiştirmek 'fail-fast' davranışıyla ConcurrentModificationException atar.
41                Pratikte silme için 'removeIf' en temiz yoldur.""");
42    }
43}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: ListIterator — çift yönlü gezinti + gezerken güncelleme/ekleme.
2// Çalıştırma: java Ornek2.java
3import java.util.ArrayList;
4import java.util.List;
5import java.util.ListIterator;
6
7public class Ornek2 {
8
9    public static void main(String[] args) {
10        List<Integer> sayilar = new ArrayList<>(List.of(1, 2, 3, 4));
11
12        // ListIterator yalnızca List'lerde vardır; ileri+geri gidebilir, set/add yapabilir.
13        ListIterator<Integer> it = sayilar.listIterator();
14
15        // İleri giderken her elemanı karesiyle DEĞİŞTİR (set).
16        while (it.hasNext()) {
17            int deger = it.next();
18            it.set(deger * deger);   // mevcut elemanı güncelle
19        }
20        System.out.println("Kareler (set ile): " + sayilar);
21
22        // Şimdi GERİ giderek yazdır (çift yönlülük).
23        System.out.print("Geri gezinti: ");
24        while (it.hasPrevious()) {
25            System.out.print(it.previous() + " ");
26        }
27        System.out.println();
28
29        // İleri giderken araya eleman EKLE (add).
30        ListIterator<Integer> it2 = sayilar.listIterator();
31        while (it2.hasNext()) {
32            int d = it2.next();
33            if (d == 4) it2.add(-1);   // 4'ten (kare: 16... burada 1,4,9,16) sonra -1 ekle
34        }
35        System.out.println("Ekleme sonrası: " + sayilar);
36        System.out.println("Index bilgisi: nextIndex=" + it2.nextIndex());
37
38        System.out.println("""
39
40                --- ListIterator ---
41                Iterator'ın List'lere özel, güçlü sürümü:
42                  - çift yönlü: hasNext/next ve hasPrevious/previous,
43                  - gezerken set(e) ile GÜNCELLEME, add(e) ile EKLEME,
44                  - nextIndex()/previousIndex() ile konum bilgisi.
45                Bir listeyi gezerken yerinde değiştirmen gerekiyorsa ListIterator idealdir.""");
46    }
47}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: Kendi Iterable'ını yaz — sınıfın for-each ile gezilebilsin.
2// Çalıştırma: java Ornek3.java
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5
6public class Ornek3 {
7
8    public static void main(String[] args) {
9        // Kendi koleksiyon benzeri sınıfımız (1..n aralığı). Iterable olduğu için for-each çalışır.
10        Aralik aralik = new Aralik(1, 5);
11        System.out.print("for-each ile: ");
12        for (int sayi : aralik) {
13            System.out.print(sayi + " ");
14        }
15        System.out.println();
16
17        // Iterable olduğu için Stream/forEach gibi araçlara da köprü kurulabilir.
18        long toplam = 0;
19        for (int s : aralik) toplam += s;
20        System.out.println("Toplam: " + toplam);
21
22        System.out.println("""
23
24                --- Kendi Iterable'ını yazmak ---
25                Bir sınıf Iterable<T> uygularsa (iterator() metodu), for-each döngüsüyle gezilebilir.
26                iterator(), Iterator<T> döndürür: hasNext() + next() (gerekirse remove()).
27                Bu, kendi veri yapılarını (ağaç, grafik, sayfa akışı...) dilin doğal döngüsüne bağlamanı sağlar.
28                JDK'daki List/Set/Map.values() hepsi bu sözleşmeyi uygular.""");
29    }
30}
31
32// Iterable<Integer>: for-each ile gezilebilir bir tam sayı aralığı.
33class Aralik implements Iterable<Integer> {
34    private final int bas, son;
35    Aralik(int bas, int son) { this.bas = bas; this.son = son; }
36
37    @Override
38    public Iterator<Integer> iterator() {
39        return new Iterator<>() {
40            private int simdiki = bas;
41            @Override public boolean hasNext() { return simdiki <= son; }
42            @Override public Integer next() {
43                if (!hasNext()) throw new NoSuchElementException();
44                return simdiki++;
45            }
46        };
47    }
48}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.