Iterable
представляет возможность предоставить Iterator
по запросу.Итак, чтобы украсить существующую итерацию с помощью логики фильтрации, вы должны реализовать декорирование Iterator
.
static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
return () -> new Iterator<T>() {
Iterator<T> sourceIterator = it.iterator();
T current;
boolean hasCurrent;
@Override
public boolean hasNext() {
while(!hasCurrent) {
if(!sourceIterator.hasNext()) {
return false;
}
T next = sourceIterator.next();
if(pred.test(next)) {
current = next;
hasCurrent = true;
}
}
return true;
}
@Override
public T next() {
if(!hasNext()) throw new NoSuchElementException();
T next = current;
current = null;
hasCurrent = false;
return next;
}
};
}
, которое вы можете протестировать с помощью
List<String> original = new ArrayList<>();
Collections.addAll(original, "foo", "bar", "baz");
Iterable<String> filter = select(original, s -> s.startsWith("b"));
System.out.println(String.join(", ", filter));
original.removeIf(s -> !s.endsWith("r"));
System.out.println(String.join(", ", filter));
Самая большая проблема при реализациитакой Iterator
, должен предоставить двум методам hasNext
и next
правильную семантику, без каких-либо гарантий относительно того, как вызывающий вызов вызовет их, т.е. вы не можете предполагать, что он никогда не вызовет hasNext()
дважды, ничто next()
всегда будет вызываться с предшествующим hasNext()
.
Та же логика может быть реализована намного проще с помощью Stream API:
static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
return () -> StreamSupport.stream(it.spliterator(), false)
.filter(pred).iterator();
}