18 Ağustos 2007

For each döngüsü ve Iterator

Bildiğiniz gibi Java 5 ile beraber for each döngüsü java'ya eklendi. Bu döngü ile bir collection'daki yada bir dizideki elemanlar üzerinde rahatça dolaşıp işlem yapabiliyorsunuz. Örneğin; elinizde String sınıfı tipinde nesneler barındıran "list" isminde bir List nesneniz olsun. Bu List üzerinde aşağıdaki gibi dolaşabilirsiniz:

for (String s : list) {
// s'i kullanan veya kullanmayan işlemler...
}
Bu örnekte type safety'de sağlanmış oluyor. Generics o biçim destekleniyor yani:). Bir diğer örnekte array'ler için. Örneğin elinizde A sınıfı tipinde "arr" isminde bir diziniz olsun, bu dizi üzerinde aşağıdaki şekilde dolaşabilirsiniz:

for (A item : arr) {
// item ile bir işlem yap...
}

Iterator java'da bildim bileli olan bir şey, Iterator ile Collection nesnesinin elemanları üzerinde dolaşabilir ve işlemler yapabilirsiniz ve o collection nesnesi de bu değişikliklerden etkilenir. Örneğin; içinde String nesneleri barındıran "mySet" isminde bir HashSet nesneniz bulunmaktadır. Bu set üzerinde aşağıdaki şekilde dolaşabilirsiniz.

Iterator iter = mySet.iterator();
while ( iter.hasNext() ) {
String s = iter.next();
// işlemlere devam et...
}

Gördüğünüz gibi "for each" yapısı Iterator'dan daha basit ve anlaşılır. Ancak Iterator'un pabucunun dama kaldırılmayacağını dün anladım.
İçinde oluşturduğum sınıf tipinde nesneler barındıran bir List üzerinde bir kaç işlem yapacaktım. Bu işlem de list elemanları üzerinde sırayla ilerleyip her elemanı bir koşuldan geçirip eğer koşul sağlanırsa o elemanı listeden kaldırmaktı. Bu işlemi for each ile aşağıdaki gibi yapmıştım:

List list = ...;
for (MyClass m : list ) {
if ( condition )
list.remove(m);
}

Bu kodu çalıştırdığımda java.util.ConcurrentModificationException aldım. Daha sonra aynı işlemi Iterator ile yaptım.

List list = ...;
Iterator iter = list.iterator();
while (iter.hasNext()) {
MyClass m = iter.next();
if ( condition )
iter.remove();
}

Bu kodda ise hata almadım. Nedeni kesin olarak bilmemekle beraber bildiğim kadarıyla Iterator bir Collection nesnesi içindeki elemanlar üzerinde pointer mantığıyla çalışıyor. For each'de ise böyle bir yapı yok, yani bir array gibi çalışıyor. Bu nedenle "for each" ile list üzerinde dolaşırken aynı anda bir nesneyi listeden kaldırmak istediğinizde nesnelerin indexleri değiştiğinden dolayı bu hatayı veriyor olabilir. Iterator ise kaldırılacak elemana point ettiğinden elemanı listeden kaldırıyor ve pointer'ını yeni nesne üzerinde konumlandırıyor, bir linked list mantığı. Siz ne dersiniz?

9 yorum:

Adsız dedi ki...

Kullanım olarak foreach yaklaşımı bir liste uzerinde gezinmek amacıyla ortaya çıkmıştır. bir operasyon yapılmaması gerektiğini zaten belirtiyor kitap veya tutoriallar.

Serkan Yıldırım dedi ki...

Ben de tecrübe ettim:)

afsina dedi ki...

ya iki defa uzunca yorum yazdim cikmadi..

Serkan Yıldırım dedi ki...

Yorumları ben yayınlıyorum. Bunu da spam yorumlar oluyor, onların önüne geçmek için yaptım. İsterseniz bir defa daha yazın...

afsina dedi ki...

http://javaadami.blogspot.com/2007/09/enhanced-for-loop-and.html

Serkan Yıldırım dedi ki...

Teşekkürler bu doyurucu bilgi için...

Muhammet YILDIZ dedi ki...

Listenin index'ini temel alan hiç bir loop'ta değişiklik yapmamak gereklidir. ConcurrentModificationException vermezse bile indexler karışacaktır. Bu sadece Foreach'e özgü değil. Yani herhangi bir döngüde eğer döngü temelini oluşturan bir liste ise ve bu listenin operasyona direk olarak tabi tutulması gerekiyorsa Exception almamak için while döngüsü kullanılabilir. Ama indexleme bozulacaktır. Bu yüzden dikkatli olmak gerekir.

Adsız dedi ki...

ben döngünün içinde remove yapmamama rağmen hata aldım? acaba nedendir?

Serkan Yıldırım dedi ki...

Detaylı bilgi A.A.A'nın verdiği linkte bulunmaktadır kanımca...