Programowanie zaawansowane #4: własny iterator

Skoro znasz zarówno typy generyczne jak i struktury danych, to możesz napisać swój iterator, który będzie przechodził po kolekcji, zgodnie z Twoją intencją. Wpierw należy zaimplementować interfejs Iterator, który zawiera dwie metody hasNext i next. Pierwsza z nich musi zwracać warunek logiczny prawda-fałsz, decydujący kiedy przerwać pętle. Druga metoda powinna decydować w jaki sposób będzie iterowana kolekcja. Do tego najłatwiej stworzyć sobie jakieś pole (w moim przypadku jest to cursor), które będzie trzymać numer elementu, który został właśnie przetworzony. U mnie dodatkowo, aby mój iterator czymś się różnił od standardowego, przetwarzam co drugi element kolekcji. Pamiętaj, że poniższy kod nie jest bezpieczny wątkowo. Klasa korzysta też ze znanego Tobie typu generycznego, dzięki czemu iterator zadziała podobnie dla każdej listy, jaką utworzysz.

public class MyIterator<T> implements Iterator<T>{
	private Integer cursor = 0;
	private List<T> list;
    
    public MyIterator(List<T> list) {
        this.list = list;
    }

	@Override
	public boolean hasNext() {
		return cursor < list.size();
	}

	@SuppressWarnings("unchecked")
	@Override
	public T next() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}

		@SuppressWarnings("unchecked")
		T value = (T) list.get(cursor);
		cursor = cursor + 2;

		return value;
	}
}

Kolejnym krokiem jest zaimplementowanie swojej kolekcji. W moim przypadku jest nią klasa MyList, która nie różni się niczym o zwykłej listy, poza użyciem wcześniej zdefiniowanego iteratora. MyList musi implementować interfejs Iterable, który zawiera sygnaturę metody iterator znanej Ci z poprzedniego ćwiczenia. Zawieram w niej wywołanie napisanego przeze mnie iteratora, ale zamiast T wstawiam konkretną klasę, która jestem typem reprezentującym elementy mojej listy (u mnie jest to Integer). Dodatkowo w konstruktorze podaję wyliczaną za pomocą metody size długość listy.

public class MyList implements Iterable<Integer> {
	 private List<Integer> list;

	 MyList(List<Integer> list) {
	  this.list = list;
	 }

	 public Iterator<Integer> iterator() {
	  return new MyIterator<>(list.size());
	 }
}

Teraz wystarczy skorzystać z wyżej utworzonej listy w metodzie main.

public class IteratorMain {

	public static void main(String[] args) {
		List<Integer> fibonacci = new ArrayList<>();
		fibonacci.add(0);
		fibonacci.add(1);
		fibonacci.add(1);
		fibonacci.add(2);
		fibonacci.add(3);
		fibonacci.add(5);
		fibonacci.add(8);
		
		System.out.println("Przeglad ciagu fibonacciego za pomoca zwyklej petli:");
		fibonacci.forEach(x -> System.out.println(x));
		
		MyList myList = new MyList(fibonacci);

		System.out.println("Przeglad ciagu fibonacciego za pomoca nowego iteratora:");
		Iterator<Integer> iterator = myList.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
}

W powyższym przykładzie stworzyłem ciąg Fibonacciego korzystając z tablicy dynamicznej. Nie jest to oczywiście najlepsza struktura dla takiego ciągu danych, ale dzięki temu zobaczysz, że faktycznie jest ona iterowana co dwa elementy.

Przeglad ciagu fibonacciego za pomoca zwyklej petli:
0
1
1
2
3
5
8
Przeglad ciagu fibonacciego za pomoca nowego iteratora:
0
1
3
8

Dodaj komentarz