Ogólny format pętli iteracyjnej:
for (T wartosc : kolekcja) { // dla każdego elementu z kolekcji dostępnego // pod zmienną wartosc typu T // gdzie T jest typem generycznym }
Powyższy zapis jest równoznaczny z uzyskaniem dla danej kolekcji iteratora (obiekt klasy Iterator<T>) oraz wykorzystania metod, które on udostępnia (np. hasNext(), next()).
Iterator<T> iterator = kolekcja.iterator(); while(iterator.hasNext()) { T wartosc = iterator.next(); // dla każdego elementu z kolekcji dostępnego // pod zmienną wartosc typu T // gdzie T jest typem generycznym }Jak utworzyć klasę, która będzie umożliwiała nam przejście przez wszystkie elementy za pomocą czy to pętli iteracyjnej for czy też while? Odpowiedź jest stosunkowo prosta. Musimy zaimplementować w naszej klasie interfejs Iterable<T> ("powiadamia" on o tym, że naszą klasę można przeglądać). Interfejs ten posiada tylko jedną metodę:
public Iterator<T> iterator();zwracającą referencję do obiektu klasy implementującej interfejs Iterator<T>. Interfejs ten posiada trzy metody:
public boolean hasNext(); // sprawdza czy są jeszcze elementy w kolekcji public T next(); // zwraca kolejny element public void remove(); // usuwa kolejny elementZazwyczaj implementuje się w pełni tylko dwie pierwsze metody. Trzecią zostawia się pustą (ma ona bezpośredni wpływ na zawartość kolekcji i nie ma sensu w ten sposób usuwać elementów, od tego powinny być metody dostępne w samej kolekcji). Zobaczmy prosty przykład wykorzystania interfejsu Iterable<T> na podstawie klasy Lista<T> odzwierciedlającej listę jednostronną jednokierunkową. Najpierw klasa pojedynczego węzła/elementu listy Wezel<T>.
/** * Węzeł - element listy * @author kodatnik.blogspot.com */ class Wezel<T> { // pole przechowujące wartość znajdującą się w węźle private T obiekt; // referencja do następnego elementu listy private Wezel<T> nastepny; // konstruktor domyślny; public Wezel() { // wywołanie konstruktora dwuparametrowego) this(null, null); } // konstruktor dwuparametrowy // wartość oraz referencja do następnego węzła public Wezel(T obiekt, Wezel<T> nastepny) { this.obiekt = obiekt; this.nastepny = nastepny; } // metoda zwraca referencję do następnego węzła public Wezel<T> pobierzNastepny() { return nastepny; } // metoda zwraca przechowywaną w węźle wartość public T pobierzObiekt() { return obiekt; } }oraz klasa główna Lista<T>:
// wykorzystujemy klasę Iterator z pakietu java.util import java.util.Iterator; /** * Przykład wykorzystania iteratora * Lista jednostronna jednokierunkowa * @author kodatnik.blogspot.com */ class ListaZaimplementowaliśmy w klasie interfejs Iterable<T> (metoda iterator()) oraz utworzyliśmy wewnętrzną prywatną klasę IteratorListy implementującą interfejs Iterator<T>. Sprawdźmy działanie naszej klasy:implements Iterable<T> { // pole przechowujące referencję do początku listy private Wezel<T> poczatek; // konstruktor bezparametrowy public Lista () { // ustawiamy początek na null (lista pusta) poczatek = null; } // metoda wstawia dane na początek listy public void wstawNaPoczatek(T dane) { // tworzymy nowy węzeł oraz ustawiamy // zmienną poczatek tak aby go wskazywała poczatek = new Wezel<T>(dane, poczatek); } // metoda usuwa element znajdujący się na początku listy // oraz zwraca referencję do niego public Wezel<T> usunZPoczatku() { // zapamiętujemy element z początku listy Wezel<T> temp = poczatek; // zmieniamy referencje początku listy // na następny element (pomijamy pierwszy) poczatek = poczatek.pobierzNastepny(); // zwracamy zapamiętaną referencję return temp; } // metoda zwraca referencję do obiektu klasy // implementującej interfejs Iterator<T> public Iterator<T> iterator() { // tworzymy nowy obiekt wewnętrznej klasy IteratorListy // i zwracamy jego referencję return new IteratorListy(); } // prywatna klasa wewnętrzna implementująca interfejs Iterator<T> private class IteratorListy implements Iterator<T> { // pole przechowujące referencję do pierwszego elementu naszej listy private Wezel<T> temp = poczatek; // metoda zwraca wartość logiczną czy są jeszcze elementy w kolekcji public boolean hasNext() { return temp != null; } // metoda zwraca wartość elementu przechowywanego w kolejnym węźle public T next() { // pobieramy wartość (obiekt typu T) T obiekt = temp.pobierzObiekt(); // przechodzimy na następny element listy temp = temp.pobierzNastepny(); // zwracamy wartość return obiekt; } // metoda usuwająca element z kolekcji public void remove() { // ciało metody puste (patrz opis) } } }
// wykorzystujemy klasę Iterator z pakietu java.util import java.util.Iterator; /** * Test listy * @author kodatnik.blogspot.com */ public class TestIteratora { public static void main (String[] args) { // tworzymy pierwszą listę (parametryzujemy typem String) Lista<String> listaPierwsza = new Lista<String>(); // dodajemy elementy na początek listy listaPierwsza.wstawNaPoczatek("Adam"); listaPierwsza.wstawNaPoczatek("Marek"); listaPierwsza.wstawNaPoczatek("Kasia"); // pobieramy iterator z listy Iterator<String> iterator = listaPierwsza.iterator(); // dopóki są jeszcze elementy while(iterator.hasNext()) { // pobieramy je i wyświetlamy je na ekranie System.out.println (iterator.next()); } // tworzymy drugą listę (parametryzujemy typem Integer) Lista<Integer> listaDruga = new Lista<Integer>(); // dodajemy elementy do początek listy listaDruga.wstawNaPoczatek(10); listaDruga.wstawNaPoczatek(45); listaDruga.wstawNaPoczatek(83); // w pętli iteracyjnej wyświetlamy po kolei wszystkie elementy for (Integer wartosc: listaDruga) { System.out.println (wartosc); } } }Uruchomiona aplikacja:
Kasia Marek Adam 83 45 10Kiedy korzystać z iteratora? Wtedy gdy nasza klasa przechowuje większą liczbę takich samych elementów, mówiąc inaczej jest kolekcją jakiś elementów. Implementacja interfejsu Iterable<T> znacznie uprości obsługę takiej klasy.
2 Komentarze - Przeglądamy kolekcję - interfejs Iterable<T>
Dobry artykuł. Ma w sobie to czego brakuje reszcie czyli użycie generyków. :)
nie ma to jak dobry blog
Prześlij komentarz
Możesz użyć niektórych tagów HTML, takich jak <b>, <i>, <u>, <a> Nie spamuj :)