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. Böylece 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, bu koleksiyon üzerinde dolaşmak için Iterator
arayüzünü uygulayan özel bir yineleyici (iterator) sınıf yazalım. Bu sınıfın avantajı ile standart for-each
ya da w
hile gibi yapılarla da koleksiyon öğelerine erişim sağlayabilirim. Bu sayede, koleksiyonun iç yapısını gizleyerek dış dünyaya sadece kontrollü ve güvenli bir gezinme imkânı sunarım. Ş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 üstlenecektir.
- 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üğü koruruz.
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. 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.
Yazılımcılar, Iterator Pattern sayesinde koleksiyonlar üzerinde gezinme işlemini soyutlayarak daha okunabilir, güvenli ve sürdürülebilir kodlar geliştirir. Koleksiyonların büyüyüp çeşitlendiği büyük ölçekli projelerde bu desenin önemi artarken, küçük ve basit uygulamalarda gereksiz karmaşaya yol açmamak 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.