How to implement this FilteringIterator?
IObjectTest is a interface with a single boolean test(Object o) method
FilteringIterator is an implementation of Iterator which is initialized with another Iterator and an IObjectTest instance: new FilteringIterator(myIterator, myTest). Your FilteringIterator will then allow iteration over 'myIterator', but skipping any objects which don't pass the 'myTest' test.
Since the "h开发者_如何学编程asNext" operation actually involve repeatly moving the underlying iterator untill reach the next matching item. The question is how can it move it iterator back since hasNext is not supposed to move the underlying iterator.
If you want to do it yourself, you can use code similar to what I've written below. However, I do recommend you use Guava's Iterators.filter(Iterator, Predicate)
public class FilteredIterator<T> implements Iterator<T> {
private Iterator<? extends T> iterator;
private Filter<T> filter;
private T nextElement;
private boolean hasNext;
/**
* Creates a new FilteredIterator using wrapping the iterator and returning only elements matching the filter.
*
* @param iterator
* the iterator to wrap
* @param filter
* elements must match this filter to be returned
*/
public FilteredIterator(Iterator<? extends T> iterator, Filter<T> filter) {
this.iterator = iterator;
this.filter = filter;
nextMatch();
}
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() {
if (!hasNext) {
throw new NoSuchElementException();
}
return nextMatch();
}
private T nextMatch() {
T oldMatch = nextElement;
while (iterator.hasNext()) {
T o = iterator.next();
if (filter.matches(o)) {
hasNext = true;
nextElement = o;
return oldMatch;
}
}
hasNext = false;
return oldMatch;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
public interface Filter<T> {
/**
* Determines whether elements should be filtered or not.
*
* @param element the element to be matched against the filter
* @return {@code true} if the element matches the filter, otherwise {@code false}
*/
public boolean matches(T element);
}
You would need to make your iterator stateful. Cache the last value you retrieved from hasNext
and use that from the next
method, if it exists.
private boolean hasCached;
private T cached;
public boolean hasNext() {
if ( hasCached ) return true;
//iterate until you find one and set hasCached and cached
}
public T next() {
if ( hasCached ) {
hasCached = false;
return cached;
}
//iterate until next matches
}
If this is homework, this won't help you, but if not: the Guava Library has the exact functionality you are after
Iterators.filter(Iterator, Predicate)
(You might have a look at how they did it for inspiration)
how about my version? the next() method could be a little easy to understand compare to the previous examples.
public class PredicateIterator implements Iterator {
private Iterator iterator;
private Predicate predicate;
private Object cached;
private boolean hasNextCached;
private boolean hasNext;
public PredicateIterator(Iterator iterator, Predicate predicate) {
this.iterator = iterator;
this.predicate = predicate;
}
@Override
public boolean hasNext() {
if (hasNextCached) {
return hasNext;
} else {
return findNextMatch();
}
}
private boolean findNextMatch() {
boolean match = false;
while(!match && iterator.hasNext()) {
cached = iterator.next();
match = predicate.test(cached);
}
hasNextCached = true;
hasNext = match;
return match;
}
@Override
public Object next() {
if (hasNext()) {
hasNextCached = false;
return cached;
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
iterator.remove();
}
}
精彩评论