Iterator filtrujacy

0

Witam, chciałbym napisać własny iterator który przyjmuje dwa parametry (lista, predykat) i filtruje zgodnie z predykatem.
W moim przypadku zwracać powinien liczby parzyste. Póki co napisałem tylko tyle i nie potrafię sobie poradzić z nullem
na końcu lub żeby nie wykroczyć poza listę.

 public class MyIterator implements Iterator<Integer>{

	private List<Integer> lista;
	private Predicate<Integer> predicat;
	private int index;

	public MyIterator(List<Integer> lista, Predicate<Integer> predicat) {
		this.lista = lista;
		this.predicat = predicat;
	}

	@Override
	public boolean hasNext() {
		return index < lista.size();
	}

	@Override
	public Integer next() {
		if(predicat.test(lista.get(index))){
    		return lista.get(index++);
    	}
		if(index == lista.size()-1){
			index++;
			return null;
		}
		index++;
		return next();
	}
}
0

Zawsze wywoływany jest hasNext() przed next(). Gdyby Twoja lista miała na końcu element który nie spełnia warunku, to hasNext() zwróci u Ciebie true mimo że tak na prawdę nie ma już elementów do zwrócenia (nie ma już elementów które spełniają warunek).

2

Pomijając to, że można to zrobić za pomocą streamów... Jak masz kolekcję, to wykorzystaj jej iterator do obsługi iteracji, a sam skup się na obsłudze filtrowania:

public class FilteringIterator<T> implements Iterator<Optional<T>> {


	private final Iterator<T> iter;
	private final Predicate<T> predicate;

	public FilteringIterator(Collection<T> collection, Predicate<T> predicate) {
		this.iter = collection.iterator();
		this.predicate = predicate;
	}

	public static void main(String[] args) {
		ArrayList<Integer> ints = new ArrayList<>();

		ints.add(1);
		ints.add(2);
		ints.add(3);
		ints.add(4);

		FilteringIterator<Integer> it = new FilteringIterator<>(ints, i -> i % 2 == 0);

		while(it.hasNext())
			it.next().ifPresent(System.out::println);
	}

	@Override
	public boolean hasNext() {
		return iter.hasNext();
	}

	@Override
	public Optional<T> next() {
		T next = iter.next();
		return Optional.ofNullable(next).filter(predicate);
	}
}

W tym przypadku gdy element został odfiltrowany z kolekcji to next zwróci Empty, które można sobie jakoś tam obsłużyć bez większego zachodu. Jeżeli element przeszedł test, to masz Optional z wartością. Można użyć ifPresent, albo get.

2

To trochę łżąca implementacja iteratora - bo najpierw potrafi obiecać, że coś ma hasNext => true
A po wyborach (czyli next() ) zwrócić empty.

IMO trzeba by obsługę predykatu przenieść do hasNext()

Co zresztą nadal da się zrobić strumieniami (np. wykorzystanie anyMatch()) (albo jeszcze lepiej originalList.stream().filter(predicate).iterator(); i nawet klasy nie trzeba robić )
Albo po prostu w oryginalnej implementacji przenieść zabawy z index++ do hasNext()

1

Tyle tylko, że wtedy masz iterator until, który zwraca dopóki spełniony jest warunek. Co jest trochę innym przypadkiem.

0

Rozwiązanie jednolinijkowe od jarekr000000 jest super szybkie i mi się podoba! :) ale w zadaniu trzeba był zrobić tą klasę więc Koziołek tutaj robi robotę! dzięki za wszystkie odpowiedzi.

2

@PabloxxOne: bo to jest dobre podejście.

@jarekr000000: ale masz konstrukcję klasyczną:


while(i.hasNext()){
	if((elem = i.next() != null){
		/// cośtam cośtam
	}    
}

Przy czym ja tu widzę inny problem. Sama definicja problemu w pierwszym poście łamie SRP. Iterator służy do chodzenia po kolekcji, a nie do operowania na obiektach tej kolekcji. Jak łączysz te dwa elementy, to masz problem, bo nie wiadomo co mają poszczególne metody robić.

1 użytkowników online, w tym zalogowanych: 0, gości: 1