Daha önceki yazılarımızda Java’nın güçlü Koleksiyonlar Çatısı’nı (Collections Framework) kullanarak verileri ArrayList, LinkedList, HashSet gibi yapılarda nasıl depolayacağımızı öğrendik. Ancak bir koleksiyonun içine verileri doldurmak işin sadece yarısıdır. Peki bu verileri nasıl okuyacağız, içlerinde nasıl arama yapacağız veya belirli koşullara uyan verileri bu listeden nasıl sileceğiz?
Standart bir dizide (array) for döngüsü ve bir indis numarası (dizi[i]) kullanarak elemanlara kolayca ulaşabiliriz. Ancak HashSet veya HashMap gibi gelişmiş koleksiyonların belirli bir sıra veya indis numarası (index) mantığı yoktur. İşte bu noktada Java, tüm koleksiyon türlerinde standart ve güvenli bir şekilde gezinmemizi (traverse) sağlayan mükemmel yapılar sunar.
Bu rehberimizde, Java’da koleksiyonlar üzerinde gezinmenin temel yollarını; Iterator, ListIterator, Gelişmiş For Döngüsü (For-Each) ve modern forEach() yaklaşımlarını örneklerle ve aralarındaki farklarla inceleyeceğiz.
1. Java’da Iterator Nedir ve Nasıl Çalışır?
Iterator, Java’da herhangi bir koleksiyonun elemanları üzerinde baştan sona doğru tek tek ilerlemenizi sağlayan, koleksiyonlardan bağımsız standart bir arayüzdür (interface). Bir koleksiyonun iç yapısı (ister bağlı liste, ister ağaç, ister karma tablosu olsun) ne kadar karmaşık olursa olsun, Iterator hepsinde aynı basit mantıkla çalışır.
Bir iteratörü, koleksiyonun üzerinde hareket eden bir imleç (cursor) veya gösterge olarak düşünebilirsiniz. Başlangıçta imleç ilk elemanın hemen “önünde” durur.
Iterator Arayüzünün Temel Metotları: Iterator arayüzü üç temel metoda sahiptir:
hasNext(): Koleksiyonda okunacak (üzerinden atlanacak) sıradaki bir eleman olup olmadığını kontrol eder. Varsatrue, yoksa (listenin sonuna gelinmişse)falsedöndürür.next(): İmleci bir sonraki elemanın üzerine atlatır ve üzerinden atladığı bu elemanı size teslim eder.remove():next()metodu ile üzerinden atlanan (okunan) son elemanı koleksiyondan kalıcı olarak siler.
Örnek: Iterator ile ArrayList Üzerinde Gezinmek
Aşağıdaki örnekte bir ürün listesi oluşturup, bu listeyi Iterator ile okuyacağız ve içindeki belirli bir ürünü sileceğiz.
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorOrnegi {
public static void main(String[] args) {
ArrayList<String> urunler = new ArrayList<String>();
urunler.add("Telefon");
urunler.add("Bilgisayar");
urunler.add("Tablet");
urunler.add("Kulaklık");
// 1. Koleksiyondan bir Iterator nesnesi elde ediyoruz
Iterator<String> gezgin = urunler.iterator();
System.out.println("Orijinal Liste:");
// 2. hasNext() ile okunacak eleman kaldığı sürece döngüyü çalıştır
while (gezgin.hasNext()) {
// 3. Sıradaki elemanı al
String urun = gezgin.next();
System.out.println(urun);
// Eğer ürün "Tablet" ise listeden sil
if (urun.equals("Tablet")) {
gezgin.remove(); // O anki elemanı güvenle siler
}
}
System.out.println("\nSilme İşleminden Sonra Liste: " + urunler);
}
}
Çıktı:
Orijinal Liste:
Telefon
Bilgisayar
Tablet
Kulaklık
Silme İşleminden Sonra Liste: [Telefon, Bilgisayar, Kulaklık]
2. İki Yönlü Gezgin: ListIterator
Standart Iterator sadece ileriye doğru hareket edebilir. Ancak Java, List arayüzünü uygulayan koleksiyonlar (örneğin ArrayList ve LinkedList) için çok daha yetenekli bir alt arayüz olan ListIterator‘ı sunar.
ListIterator, Iterator‘ın tüm özelliklerine sahip olmasının yanı sıra, listede geriye doğru gitmenize ve gezinirken yeni elemanlar ekleyip mevcutları değiştirmenize olanak tanır.
Ekstra Metotları:
hasPrevious()veprevious(): TıpkıhasNext()venext()gibi çalışır ancak listede geriye doğru hareket eder.add(E obj): İmlecin bulunduğu mevcut konuma anında yeni bir eleman ekler.set(E obj):next()veyaprevious()ile okunan son elemanın değerini yeni bir değerle günceller.
Örnek: ListIterator ile Listeyi Tersten Okumak ve Değiştirmek
import java.util.ArrayList;
import java.util.ListIterator;
public class ListIteratorOrnegi {
public static void main(String[] args) {
ArrayList<String> harfler = new ArrayList<>();
harfler.add("A");
harfler.add("B");
harfler.add("C");
// ListIterator oluşturuluyor
ListIterator<String> listGezgin = harfler.listIterator();
// Önce listeyi baştan sona gezelim ve her elemanın yanına "+" koyalım
while (listGezgin.hasNext()) {
String harf = listGezgin.next();
listGezgin.set(harf + "+"); // Okunan elemanı değiştir
}
System.out.println("İleri doğru gidip değiştirdikten sonra:");
// Şu an imleç listenin en sonunda. Şimdi geriye doğru okuyalım.
while (listGezgin.hasPrevious()) {
String harf = listGezgin.previous();
System.out.print(harf + " ");
}
}
}
3. Gelişmiş For Döngüsü (For-Each Loop)
Java 5 sürümüyle birlikte, iteratörlerin yazım karmaşıklığını ortadan kaldırmak ve koleksiyon/dizi üzerinde gezinmeyi son derece basitleştirmek için “For-Each” (Gelişmiş For) döngüsü dile eklenmiştir. Arka planda gizlice bir Iterator kullanan bu döngü, koleksiyonun içindeki elemanları ilk elemandan son elemana kadar otomatik olarak sıralı şekilde okur.
Eğer niyetiniz sadece listedeki verileri “okumak” ise, For-Each döngüsü en temiz ve modern yaklaşımdır.
Örnek: For-Each Kullanımı
import java.util.HashSet;
public class ForEachOrnegi {
public static void main(String[] args) {
HashSet<Integer> sayilar = new HashSet<>();
sayilar.add(10);
sayilar.add(20);
sayilar.add(30);
int toplam = 0;
// "sayilar koleksiyonundaki her bir int 'sayi' için bu döngüyü çalıştır"
for (int sayi : sayilar) {
toplam += sayi;
System.out.println("Okunan Sayı: " + sayi);
}
System.out.println("Sayıların Toplamı: " + toplam);
}
}
Yukarıdaki kodun ne kadar kısa ve okunabilir olduğuna dikkat edin. Bir Iterator oluşturmamıza veya hasNext() yazmamıza hiç gerek kalmadı.
4. Iterator ve For-Each Arasındaki Hayati Fark: Ne Zaman Hangisi?
Yeni başlayanların en sık sorduğu soru şudur: “For-Each bu kadar kolaysa, neden hala eski Iterator yapısını öğrenmek zorundayım?”
Cevap çok kritiktir: Güvenli Silme ve Değiştirme İşlemleri.
Eğer bir koleksiyonu sadece okuyacaksanız veya ekrana yazdıracaksanız, her zaman For-Each döngüsünü tercih etmelisiniz. Ancak, koleksiyon üzerinde gezinirken bir elemanı silmeniz veya listeye yeni eleman eklemeniz gerekiyorsa, For-Each döngüsü çöker.
Java’da bir for-each döngüsü içindeyken koleksiyona doğrudan müdahale edip koleksiyon.remove(eleman) komutunu çağırırsanız, Java eşzamanlı modifikasyon hatası olan ConcurrentModificationException fırlatır ve programınız durur. Bu bir güvenlik mekanizmasıdır; çünkü döngü arka planda listedeki elemanları sayarken siz aniden sayıyı değiştirmiş olursunuz.
İşte gezinme (traverse) sırasında eleman silmek istediğiniz durumlarda mecburen geleneksel Iterator kullanmalısınız. Iterator.remove() metodu, koleksiyonun yapısını bozmadan güvenli silme işlemi yapar.
5. Java 8 ile Gelen Modern Çözüm: forEach() ve Lambda İfadeleri
JDK 8 ile birlikte koleksiyonlar üzerinde gezinme mantığı, “Fonksiyonel Programlama” ve “Lambda İfadeleri” (Lambda Expressions) sayesinde yepyeni bir boyuta taşınmıştır. Iterable arayüzüne eklenen yeni forEach() metodu sayesinde döngü kurmadan, tek bir satırda harika işler yapabilirsiniz.
Örnek: Lambda ile forEach Kullanımı
import java.util.ArrayList;
public class LambdaForEachOrnegi {
public static void main(String[] args) {
ArrayList<String> isimler = new ArrayList<>();
isimler.add("Ahmet");
isimler.add("Mehmet");
isimler.add("Ayşe");
// Geleneksel yaklaşımların tümünü bir kenara bırakan,
// Java 8 Lambda ifadesi ile tek satırda koleksiyonu yazdırma:
isimler.forEach(isim -> System.out.println("Listede bulunan: " + isim));
}
}
Bu metot, listenin içindeki her bir isim elemanını alır ve -> (ok/arrow) operatörünün sağ tarafındaki kod bloğuna aktararak çalıştırır.
Sonuç
Java’da koleksiyonlar üzerinde veri okumak, veri silmek veya elemanlar arasında gezinmek algoritmalarınızın kalbini oluşturur. Kısaca özetlemek gerekirse;
- Sadece elemanları okuyup işlem yapacaksanız For-Each döngüsünü,
- Okuma esnasında listeyi manipüle edecek veya eleman silecekseniz
Iteratorarayüzünü, - Listede hem ileri hem de geriye doğru gitmeniz gerekiyorsa
ListIteratorarayüzünü, - Kodunuzun çok daha kısa, modern ve fonksiyonel olmasını istiyorsanız Java 8’in
forEach()yapısını tercih etmelisiniz.
Bu yöntemleri kendi projelerinizde kullanarak hangi yöntemin hangi senaryo için en uygun olduğunu kolaylıkla kavrayabilirsiniz!





