Yazılım geliştirirken, koleksiyonlar üzerinde gezinmek çoğu zaman temel bir ihtiyaç hâline gelir. Bu ihtiyaca yönelik en yaygın çözümlerden biri, Iterator patterndir. Bu deseni kullanarak koleksiyonların içeriğini tek tek dolaşırım ve veri yapısının iç detaylarına bağlı kalmadan elemanlara doğrudan erişirim.
Iterator Pattern, Davranışsal (Behavioral) tasarım desenleri arasında yer alır. Temel amacı, bir koleksiyonun içindeki nesneler üzerinde sırayla dolaşmayı sağlar ve bunu yaparken koleksiyonun nasıl yapılandığıyla ilgilenmem gerekmez. Bu sayede kod daha esnek ve sürdürülebilir hâle gelecektir.
Java, bu deseni standart kütüphanelerinde başarıyla uygular. java.util.Iterator
arayüzü, bu desenin doğrudan bir yansımasıdır. Koleksiyon sınıfları (ArrayList
, HashSet
gibi), bu arayüzü destekleyerek elemanları güvenli ve temiz bir şekilde dolaşmama olanak tanır.

İçerik
Iterator Pattern’ın Bileşenleri
Iterator Pattern genellikle şu dört bileşeni içerir:
- Iterator Arayüzü –
hasNext()
,next()
gibi gezinme yöntemlerini tanımlar. - Concrete Iterator (Somut Yineleyici) – Belirli bir koleksiyon türü için
Iterator
arayüzünü uygular. - Aggregate Arayüzü – Koleksiyonun
Iterator
nesnesi üretmesini sağlar. - Concrete Aggregate (Somut Koleksiyon) – Koleksiyonu tutar ve
Iterator
döner.
Kendi koleksiyon sınıfımı oluşturduğumda, onun üzerinde gezinmek için de kendi yineleyicimi yazabilirim. Şimdi bu yapıyı örnek bir senaryo ile inceleyelim.
Kullanım Örneği
// Koleksiyonum
class Kitap {
String isim;
public Kitap(String isim) {
this.isim = isim;
}
public String getIsim() {
return isim;
}
}
// Iterator Arayüzü
interface KitapIterator {
boolean hasNext();
Kitap next();
}
// Concrete Iterator
class KitapListesiIterator implements KitapIterator {
private Kitap[] kitaplar;
private int pozisyon = 0;
public KitapListesiIterator(Kitap[] kitaplar) {
this.kitaplar = kitaplar;
}
public boolean hasNext() {
return pozisyon < kitaplar.length;
}
public Kitap next() {
return kitaplar[pozisyon++];
}
}
// Concrete Aggregate
class KitapListesi {
private Kitap[] kitaplar;
public KitapListesi() {
kitaplar = new Kitap[] {
new Kitap("Design Patterns"),
new Kitap("Clean Code"),
new Kitap("Effective Java")
};
}
public KitapIterator iterator() {
return new KitapListesiIterator(kitaplar);
}
}
public class Main {
public static void main(String[] args) {
KitapListesi liste = new KitapListesi();
KitapIterator iterator = liste.iterator();
while (iterator.hasNext()) {
Kitap kitap = iterator.next();
System.out.println(kitap.getIsim());
}
}
}
Iterator Tasarım Deseninin Avantajları
- Soyutlama Sağlar: Koleksiyonun iç yapısını bilmeden onun üzerinde gezinmeme olanak tanır. Örneğin, bir
ArrayList
mi yoksaHashSet
mi olduğunu önemsemeden her bir elemanı sırayla alabilirim. - Tek Sorumluluk İlkesine Uyar: Koleksiyonun veri tutma sorumluluğu ile gezinme sorumluluğunu birbirinden ayırır. Koleksiyon sadece veriyi saklar, iterator ise bu verinin üzerinde gezinmeyi üstlenir.
- Kod Tekrarını Azaltır: Farklı koleksiyon türleri için aynı gezinme mantığını kullanmamı sağlar. Bu da kodun yeniden kullanılabilirliğini artırır.
- Genişletilebilirlik Sunar: Yeni koleksiyon türleri eklediğimde sadece ona özel bir iterator yazmam yeterli olur. Mevcut yapıları değiştirmeme gerek kalmaz.
- Güvenli Gezinme: Iterator nesnesi, koleksiyonun yapısını dışarıdan değiştirmeden dolaşmaya olanak tanır. Böylece veri bütünlüğü korunur.
Iterator Design Pattern Dezavantajları
- Ekstra Sınıflar ve Karmaşıklık: Küçük projelerde her koleksiyon için özel bir iterator sınıfı yazmak gereksiz karmaşıklık yaratabilir.
- Geriye Doğru Gezinme Zorluğu: Standart
Iterator
arayüzü ileriye doğru gezinmeyi destekler. Geriye doğru gitmek içinListIterator
gibi özel arayüzlere ihtiyaç duyarım. - Eşzamanlılık Sorunları: Koleksiyon üzerinde gezinirken başka bir işlem koleksiyonu değiştirirse
ConcurrentModificationException
hatası alabilirim. Bunu önlemek için eşzamanlı koleksiyon yapıları ya daCopyOnWriteArrayList
gibi alternatifler kullanmalıyım.
Nerelerde Kullanabiliriz?
- Özel Koleksiyon Yapıları: Kendi veri yapılarımı yazdığımda (örneğin, bir ağaç, grafik ya da dairesel liste gibi), bu yapıların üzerinde standart bir şekilde gezinmek için iterator kullanırım.
- API Tasarımları: Kütüphane geliştirirken, kullanıcıların koleksiyonlarım üzerinde rahatça gezinebilmelerini sağlamak için bu deseni uygularım.
- Veri Akışı Senaryoları: Büyük veri setleri üzerinde satır satır işlem yaparken her veriyi belleğe almadan iterator ile işlem yaparım.
- Yaygın Koleksiyon Kullanımı:
for-each
döngüsünün Java’da çalışmasını sağlayan mekanizma da aslındaIterator
arayüzüne dayanır. Örneğin:
for (String isim : isimListesi) {
System.out.println(isim);
}
Bu kullanımın arka planında Iterator
çalışır.
Sonuç
Iterator Pattern, karmaşık veri yapılarında gezinmemi sağlar ve koleksiyonun iç yapısına müdahale etmeden bu işlemi gerçekleştirmeme olanak tanır. Bu desen, özellikle kendi özel koleksiyon sınıflarımı oluşturduğumda veya var olan yapılar üzerinde soyutlama sağlamak istediğimde bana büyük kolaylık sunar. Kodun okunabilirliğini artırır, bakımını kolaylaştırır ve yazılımın esnekliğini önemli ölçüde yükseltir.
Iterator Pattern, yazılımda gezinme sorumluluğunu soyutlayarak koleksiyonlar üzerinde daha okunabilir, güvenli ve sürdürülebilir kod yazmamı sağlar. Büyük ölçekli projelerde, koleksiyonlar büyüyüp çeşitlendikçe bu desenin değeri daha da artar. Ancak küçük ve basit uygulamalarda, gereksiz karmaşaya neden olmamak için dikkatli kullanılmalıdır.
Kod yapısını temiz tutmak, sorumlulukları ayırmak ve esnek sistemler kurmak istiyorsan, Iterator Pattern’ı mutlaka araç kutunda bulundurmalısın.