1. Iterable interfeysi
Java-da demək olar ki, bütün kolleksiyalar (Map istisna olmaqla) Iterable interfeysini reallaşdırır. Bu o deməkdir ki, onlardan ardıcıl — element-addım qaydasında, daxili quruluşa varmadan keçmək olar. Proqramçı üçün bu belə görünür: “kolleksiyanın bütün elementlərdən keçmək üçün daxili üsulu var”.
Iterable interfeysi cəmi bir metodu müəyyənləşdirir:
Iterator<E> iterator();
iterator() metodu Iterator tipində obyekt qaytarır — kolleksiyadan addım-addım necə keçməyi bilən “köməkçi”. Bu sayədə vərdiş etdiyimiz for-each dövrü işləyir:
for (ElementType e : collection) {
// ...
}
— pərdə arxasında məhz həmin Iterator gizlənir. Əlavə üstünlük: onun köməyi ilə keçid zamanı remove() metodu ilə elementləri təhlükəsiz silmək olar. Bunu adi keçidlə etməyə çalışsanız, asanlıqla ConcurrentModificationException ala bilərsiniz.
2. Iterator interfeysi
Iterator — kolleksiyanızla gedə bilən, elementləri ötürməyən və onun keçid qaydasına riayət edən “kuryer”dir.
| Metod | Təsvir |
|---|---|
|
Dolanmaq üçün hələ element varmı? |
|
Növbəti elementi qaytarmaq və ona keçmək |
|
Cari elementi təhlükəsiz, problemsiz silmək |
Kolleksiyanı Iterator ilə dolaşma nümunəsi
import java.util.*;
public class IteratorDemo {
public static void main(String[] args) {
List<String> tasks = new ArrayList<>();
tasks.add("Pişiyi sığallamaq");
tasks.add("Ev tapşırığını etmək");
tasks.add("Seriala baxmaq");
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
System.out.println("Tapşırıq: " + task);
}
}
}
Burada nə baş verir?
- tasks.iterator() vasitəsilə iterdatoru əldə edirik.
- Növbəti element olduğu müddətcə (hasNext() true qaytaranda), onu next() ilə götürüb çıxarırıq.
- İterator keçid qaydasını özü qoruyur — kolleksiyanın elementləri daxilində necə saxladığını bilməyiniz lazım deyil.
3. Dövr operatorları varkən nə üçün Iterator lazımdır?
Iterator sayəsində istənilən kolleksiyanı, hətta indekssiz (məsələn, Set) də dolaşmaq olar. Bu, kolleksiyanın konkret tipindən asılı olmayan universal üsuldur.
Elementlərin təhlükəsiz silinməsi
Tez-tez rast gəlinən bir vəzifə: kolleksiyadan keçmək və bəzi elementləri silmək. Bunu for-each ilə etsəniz, xəta ala bilərsiniz:
for (String task : tasks) {
if (task.contains("pişik")) {
tasks.remove(task); // BAM! ConcurrentModificationException
}
}
Bu niyə baş verir? Kolleksiya, iteratordan başlayan keçid zamanı onun strukturunun birbaşa dəyişdirilməsini gözləmir.
Düzgün üsul:
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
if (task.contains("pişik")) {
it.remove(); // Hər şey problemsiz keçəcək!
}
}
Niyə sadəcə indekslərdən istifadə etmək olmur?
Çünki bütün kolleksiyalarda indeks yoxdur. Məsələn, HashSet və ya TreeSet-də “beşinci element” anlayışı yoxdur. Iterator hər zaman işləyir — gücü də bundadır.
4. for-each haqqında detalları
Təkmilləşdirilmiş for dövrü (for-each) hələ Java 5-də ortaya çıxıb. Əslində bu, elementləri maksimum sadəliklə dolaşmağa imkan verən sintaktik şəkərdir:
for (String task : tasks) {
System.out.println("Tapşırıq: " + task);
}
Kulis arxasında kompilyator iterator() çağırır, elementləri hasNext() ilə yoxlayır və next() vasitəsilə çıxarır. Sözün əsl mənasında insan dilindəki ifadə kimi oxunur: “siyahıdakı hər bir tapşırıq üçün”.
for-each nə vaxt uyğun olmur?
- Keçid zamanı elementləri silmək lazımdır (for-each birbaşa remove() çağırmağa imkan vermir).
- Məsələn, elementi mövqe üzrə əvəz etmək üçün indeksə çıxış lazımdır.
- Siz Map ilə işləyirsiniz — onda “açar-dəyər” cütləri var, dolaşmaq üçün ayrıca məntiq tələb olunur.
5. Map üzrə dolaşma: incəliklər və nüanslar
Map interfeysi birbaşa Iterable-i reallaşdırmır, çünki o, “açar-dəyər” cütlərindən ibarətdir. Bununla belə, Map dolaşma üçün əlverişli təqdimatlar verir.
Açarlar üzrə dolaşma
Map<String, String> users = new HashMap<>();
users.put("vasya", "vasya@example.com");
users.put("petya", "petya@gmail.com");
for (String login : users.keySet()) {
System.out.println("Login: " + login);
}
Dəyərlər üzrə dolaşma
for (String email : users.values()) {
System.out.println("Email: " + email);
}
Cütlər üzrə dolaşma (açar-dəyər)
Ən universal üsul — entrySet() üzrə dolaşmadır:
for (Map.Entry<String, String> entry : users.entrySet()) {
System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}
Maraqlı fakt: Entry — Map-in daxili interfeysidir və getKey() və getValue() metodlarına malikdir. Bu sayədə cütün hər iki hissəsini dərhal alırsınız.
Iterator vasitəsilə dolaşma
Iterator<Map.Entry<String, String>> it = users.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
// Hətta elementi təhlükəsiz silmək də olar:
if (entry.getKey().startsWith("v")) {
it.remove();
}
}
6. Həyatdan nümunələr: kolleksiyaları dolaşmaq tətbiqdə necə kömək edir
Nümunə: istifadəçinin bütün tapşırıqlarını çıxarırıq
List<String> tasks = new ArrayList<>();
tasks.add("Ev tapşırığını etmək");
tasks.add("Pişiyi sığallamaq");
tasks.add("Seriala baxmaq");
System.out.println("Bugünkü tapşırıqlarınız:");
for (String task : tasks) {
System.out.println("- " + task);
}
İndi “pişik” sözünü ehtiva edən bütün tapşırıqları silək:
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
if (task.contains("pişik")) {
it.remove();
}
}
System.out.println("Qalan tapşırıqlar:");
for (String task : tasks) {
System.out.println("- " + task);
}
Nümunə: unikal loginləri Set vasitəsilə dolaşmaq
Set<String> logins = new HashSet<>();
logins.add("vasya");
logins.add("petya");
logins.add("masha");
for (String login : logins) {
System.out.println("İstifadəçi: " + login);
}
Diqqət: Set-də çıxışın sırası istənilən ola bilər!
Nümunə: istifadəçiləri göstərmək üçün Map üzrə dolaşma
Map<String, String> users = new HashMap<>();
users.put("vasya", "vasya@example.com");
users.put("petya", "petya@gmail.com");
for (Map.Entry<String, String> entry : users.entrySet()) {
System.out.println("Login: " + entry.getKey() + ", Email: " + entry.getValue());
}
7. Iterator.remove(): elementlərin təhlükəsiz silinməsi
Yeni başlayanların ən çox etdiyi səhvlərdən biri — for-each ilə dolaşma zamanı kolleksiya elementlərini silməyə cəhd etməkdir. İterator bu problemi remove() vasitəsilə həll edir.
Bu necə işləyir?
- it.remove() çağırıldıqda cari element silinir — son next() tərəfindən qaytarılan element.
- Bu, təhlükəsizdir: kolleksiya ConcurrentModificationException atmır.
Nümunə:
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
int n = it.next();
if (n % 2 == 0) {
it.remove(); // Bütün cüt ədədləri silirik
}
}
System.out.println(numbers); // [1, 3, 5]
Kolleksiyanı dolaşma sxemi
+---------+ +---------+ +---------+
| Element | --> | Element | --> | Element | ...
+---------+ +---------+ +---------+
^ ^
| |
next() next()
İterator, hasNext() false qaytarana qədər elementlər üzrə “addımlayır”.
9. Iterator və kolleksiyaların dolaşılması ilə işləyərkən tipik səhvlər
Səhv №1: kolleksiyanı for-each ilə dolaşarkən onu modifikasiya etmək.
Elementi birbaşa for-each daxilində silməyə cəhd ConcurrentModificationException-a gətirib çıxarır:
for (String task : tasks) {
if (task.contains("pişik")) {
tasks.remove(task); // BAM! ConcurrentModificationException
}
}
Iterator və onun remove()-undan istifadə edin.
Səhv №2: remove() çağırışını next()-dən əvvəl etmək.
Əvvəlcə cari elementi next() vasitəsilə almaq lazımdır, əks halda iteratorda nəyi silmək lazım olduğu aydın deyil.
Iterator<String> it = tasks.iterator();
it.remove(); // Səhv! Əvvəlcə next() lazımdır
Səhv №3: Map-i birbaşa for-each ilə dolaşmağa cəhd.
Map birbaşa Iterable-i reallaşdırmır — keySet(), values() və ya entrySet()-dən istifadə edin.
Map<String, String> users = new HashMap<>();
// for (String entry : users) { ... } // Səhv: belə olmaz
for (Map.Entry<String, String> e : users.entrySet()) {
// düzgün
}
Səhv №4: iteratordan istifadə edərək dolaşma zamanı kolleksiyanı iteratordan kənar dəyişmək.
Keçid zamanı elementləri yalnız it.remove() ilə silin, kolleksiyanın metodları ilə deyil.
Iterator<String> it = tasks.iterator();
while (it.hasNext()) {
String task = it.next();
if (task.contains("pişik")) {
tasks.remove(task); // Səhv! it.remove() lazımdır
}
}
GO TO FULL VERSION