Iterators.filter and remove

David Conrad drconrad at gmail.com
Tue May 22 04:36:03 PDT 2012


It is not possible to remove an element from a collection while iterating
with the iterator from java.util.Iterators.filter().

For some time now I've had a class in my personal toolkit called Filterable
which is a filtered Iterable, and which allows remove when possible. It
isn't likely to be at all common to remove items while iterating over a
filtered collection, but there's no reason not to pass the remove call back
to the upstream source, if possible. Submitted here as food for thought:

package in.digo.func;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class Filterable<T> implements Iterable<T> {
    private final Iterable<T> iterable;
    private final Predicate<T> predicate;

    public static <T> Iterable<T> filter(Iterable<T> iterable,
            Predicate<T> predicate) {
        return new Filterable<>(iterable, predicate);
    }

    public Filterable(Iterable<T> iterable, Predicate<T> predicate) {
        this.iterable = iterable;
        this.predicate = predicate;
    }

    public Iterator<T> iterator() {
        return new Iterator<T>() {
            private Iterator<T> source = iterable.iterator();
            private boolean nextReady = false;
            private boolean canRemove = false;
            private T cache = null;

            @Override public boolean hasNext() {
                if (!nextReady) {
                    canRemove = false;
                    while (!nextReady && source.hasNext()) {
                        T next = source.next();
                        if (predicate.accept(next)) {
                            cache = next;
                            nextReady = true;
                        }
                    }
                }
                return nextReady;
            }

            @Override public T next() {
                if (nextReady || hasNext()) {
                    canRemove = true;
                    nextReady = false;
                    T next = cache;
                    cache = null;
                    return next;
                }
                throw new NoSuchElementException("no more elements");
            }

            @Override public void remove() {
                if (canRemove) {
                    source.remove();
                    canRemove = false;
                } else throw new IllegalStateException("cannot remove now");
            }
        };
    }
}

One other difference, besides passing along the remove call when canRemove,
is the use of separate local "next" variables apart from the "cache"
instance variable, and setting the latter to null before returning. This
can avoid an unintentional object retention if client code, for some
inexplicable reason, keeps the iterator around after removing a
sought-after item. A fine point, I'll admit.

Filterable is based on this Predicate, not the one in java.util.functions:

package in.digo.func;

public interface Predicate<T> {
    boolean accept(T t);
}

I quite prefer accept to eval, for what it's worth.

Cheers,
David


More information about the lambda-dev mailing list