Wyobraź sobie, że masz pudełko z książkami, szufladę z płytami CD i regał z grami. Każde „pojemnik” ma inną strukturę, ale chcesz przeglądać ich zawartość w ten sam sposób. Iterator Pattern rozwiązuje dokładnie ten problem w programowaniu.
## Dlaczego to ważne?
Iterator Pattern jest fundamentem dla pętli for-each w Javie i podstawą Java Collections Framework. Bez zrozumienia tego wzorca nie będziesz mógł efektywnie pracować z listami, setami czy mapami. To jeden z najczęściej używanych wzorców w codziennym programowaniu.
Co się nauczysz:
- Czym jest Iterator Pattern i jak działa
- Różnica między Iterator a pętlą for-each
- Jak bezpiecznie usuwać elementy podczas iteracji
- Praktyczne zastosowania w ArrayList, LinkedList i HashMap
- Kiedy używać każdego podejścia
- Najczęstsze błędy i jak ich uniknąć
## Czym jest Iterator Pattern?
Iterator Pattern to wzorzec behawioralny, który dostarcza sposób na sekwencyjne dostępowanie do elementów kolekcji bez ujawniania jej wewnętrznej reprezentacji. W Javie realizowany jest przez interfejs Iterator.
// Interfejs Iterator w Javie public interface Iterator{ boolean hasNext(); // Czy są jeszcze elementy? E next(); // Pobierz następny element void remove(); // Usuń bieżący element (opcjonalne) } // Interfejs Iterable - każda kolekcja go implementuje public interface Iterable { Iterator iterator(); // Zwróć iterator dla tej kolekcji }
## Iterator w praktyce – ArrayList
Zobaczmy jak Iterator działa z najpopularniejszą kolekcją – ArrayList:
import java.util.*; public class IteratorExample { public static void main(String[] args) { // Tworzymy listę miast ArrayListmiasta = new ArrayList<>(); miasta.add("Warszawa"); miasta.add("Kraków"); miasta.add("Gdańsk"); miasta.add("Wrocław"); // Sposób 1: Iterator ręczny System.out.println("=== Iterator ręczny ===" ); Iterator iterator = miasta.iterator(); while (iterator.hasNext()) { String miasto = iterator.next(); System.out.println("Miasto: " + miasto); } // Sposób 2: Pętla for-each (używa Iterator pod spodem) System.out.println("=== Pętla for-each ===" ); for (String miasto : miasta) { System.out.println("Miasto: " + miasto); } } }
## Bezpieczne usuwanie elementów
Jedna z najważniejszych zalet Iterator to możliwość bezpiecznego usuwania elementów podczas przeglądania kolekcji:
import java.util.*; public class SafeRemovalExample { public static void main(String[] args) { ArrayListliczby = new ArrayList<>(); for (int i = 1; i <= 10; i++) { liczby.add(i); } // ❌ BŁĄD - modyfikacja podczas for-each // To spowoduje ConcurrentModificationException! /* for (Integer liczba : liczby) { if (liczba % 2 == 0) { liczby.remove(liczba); // BŁĄD! } } */ // ✅ POPRAWNIE - użyj Iterator.remove() Iterator iterator = liczby.iterator(); while (iterator.hasNext()) { Integer liczba = iterator.next(); if (liczba % 2 == 0) { iterator.remove(); // Bezpieczne usuwanie System.out.println("Usunięto: " + liczba); } } System.out.println("Pozostałe liczby: " + liczby); // Wynik: [1, 3, 5, 7, 9] } }
## Iterator z różnymi kolekcjami
Iterator działa jednakowo niezależnie od typu kolekcji:
import java.util.*; public class DifferentCollectionsExample { public static void main(String[] args) { // ArrayList - lista z dostępem przez indeks ListarrayList = new ArrayList<>(); arrayList.add("Java"); arrayList.add("Python"); arrayList.add("JavaScript"); // LinkedList - lista dwukierunkowa List linkedList = new LinkedList<>(); linkedList.add("Spring"); linkedList.add("Hibernate"); linkedList.add("Maven"); // HashSet - zbiór unikalnych elementów Set hashSet = new HashSet<>(); hashSet.add("HTML"); hashSet.add("CSS"); hashSet.add("Bootstrap"); // Jedna metoda obsługuje wszystkie kolekcje! System.out.println("Języki programowania:"); printCollection(arrayList); System.out.println("Technologie Java:"); printCollection(linkedList); System.out.println("Technologie frontend:"); printCollection(hashSet); } // Uniwersalna metoda - działa z każdą kolekcją public static void printCollection(Iterable collection) { for (String element : collection) { System.out.println("- " + element); } } }
## Iterator z HashMap
HashMap nie implementuje Iterable bezpośrednio, ale można iterować po jej elementach:
import java.util.*; public class HashMapIteratorExample { public static void main(String[] args) { Mapoceny = new HashMap<>(); oceny.put("Matematyka", 5); oceny.put("Fizyka", 4); oceny.put("Chemia", 3); oceny.put("Biologia", 5); // Sposób 1: Iteracja po kluczach System.out.println("=== Iteracja po kluczach ===" ); for (String przedmiot : oceny.keySet()) { System.out.println(przedmiot + ": " + oceny.get(przedmiot)); } // Sposób 2: Iteracja po wartościach System.out.println("=== Iteracja po wartościach ===" ); for (Integer ocena : oceny.values()) { System.out.println("Ocena: " + ocena); } // Sposób 3: Iteracja po parach klucz-wartość (najefektywniej) System.out.println("=== Iteracja po Entry ===" ); for (Map.Entry entry : oceny.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } } }
## Kiedy używać Iterator vs for-each?
Sytuacja | Użyj | Dlaczego |
---|---|---|
Przeglądanie bez modyfikacji | for-each | Czytelniejszy kod |
Usuwanie elementów | Iterator | Bezpieczne usuwanie |
Kontrola nad iteracją | Iterator | Możliwość zatrzymania/wznowienia |
Zagnieżdżone pętle | for-each | Lepszy performance |
Przetwarzanie warunkowe | Iterator | Większa kontrola |
## Najczęstsze błędy
// ❌ BŁĄD - może wywołać wyjątek Iteratoriterator = lista.iterator(); String pierwszy = iterator.next(); // Co jeśli lista jest pusta? // ✅ POPRAWNIE - zawsze sprawdzaj hasNext() Iterator iterator = lista.iterator(); if (iterator.hasNext()) { String pierwszy = iterator.next(); System.out.println(pierwszy); } else { System.out.println("Lista jest pusta"); }
Nie, for-each używa Iterator pod spodem. Performance jest identyczny, ale for-each ma czytelniejszą składnię.
Tak, każde wywołanie iterator() tworzy nowy, niezależny Iterator. Każdy ma własny stan iteracji.
Jeśli wywołasz next() gdy nie ma więcej elementów, dostaniesz NoSuchElementException. Zawsze sprawdzaj hasNext() przed next().
Zwykłe tablice nie implementują Iterable, ale for-each działa dzięki specjalnej obsłudze w kompilerze. Do tablic używaj for-each lub tradycyjnej pętli for.
ListIterator pozwala na iterację w obu kierunkach i modyfikację elementów. Używaj go z List gdy potrzebujesz więcej możliwości niż podstawowy Iterator.
## Przydatne zasoby
– [Oracle Java Iterator Documentation](https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html)
– [Java Collections Framework Guide](https://docs.oracle.com/javase/tutorial/collections/)
– [Effective Java – Best Practices](https://www.oracle.com/technetwork/java/effectivejava-136174.html)
🚀 Zadanie dla Ciebie
Stwórz program, który:
- Wczytuje listę słów od użytkownika
- Używa Iterator do usunięcia wszystkich słów krótszych niż 5 znaków
- Wyświetla pozostałe słowa używając pętli for-each
- Zmierz czas wykonania dla 1000 słów
Bonus: Porównaj wydajność Iterator.remove() vs tworzenie nowej listy z elementami spełniającymi warunek.
Czy używasz Iterator w swoich projektach? Podziel się w komentarzach swoimi doświadczeniami z przeglądaniem kolekcji!