Bernie Noel
Bernie Noel

Reputation: 304

Filter out list of elements in a "lazy" way

I have a list of objects and I want to create another list, which will include only objects for which the method acceptable() returns "true". My problem is that I want to make this selection done only on demand. In other words, I want a new list to be calculated and populated only when its iterator() method is called. Are there any libraries for that in Java?

I'm using JDK7

Upvotes: 4

Views: 260

Answers (4)

Ramon Rivas
Ramon Rivas

Reputation: 78

Google Guava has a lot of helper methods for that type of functionality. What you want is already implemented.

Iterators.filter if you want to fiter an Iterator from a collection.

Iterables.filter if you need an instance of Iterable, for example to use it in "for-each" type of loops.

There is also Collections2.filter if you need also other collection methods to be preprocessed with the filter.

Upvotes: 4

Assaf
Assaf

Reputation: 1370

I don't know if there are libraries to achieve this, but you could make some sort of list wrapper that does something like this:

public class ResolveList<T extends MyClass> implements Iterable<T> {
//T extends MyClass for access to MyClass.acceptable()
    private final List<T> rawList;
    private List<T> processedList;
    public List<T> getList() {
        if(processedList == null) {
            processList();
        }
        return processedList; //check if null
    }

    public ResolveList(List<T> list) {
        this.rawList = list;
    }

    private void processList() {
        processedList = new ArrayList<T>(); //or whatever kind of list you prefer.
        for(T t : rawList) {
            if(t.acceptable()) {
                processedList.add(t);
            }
        }
    }

    @Override
    public Iterator<T> iterator() {
        return this.getList().iterator();
    }
}

You could use the built in iterator or get the list itself. Alternatively you could remove the getList() function and implement the List<> interface yourself.

Upvotes: 1

Raffaele
Raffaele

Reputation: 20885

Don't know if there's a library for this, but it's a simple task. This is a LazyIterator:

public class LazyIterator<E extends Acceptable> implements Iterator<E> {
    private final Iterator<E> iterator;
    private boolean hasNext;
    private E next;

    public LazyIterator(Iterator<E> iterator) {
        this.iterator = iterator;
        iterate();
    }

    private void iterate() {
        hasNext = false;
        while (iterator.hasNext()) {
            next = iterator.next();
            if (next.accept()) {
                hasNext = true;
                break;
            }
        }
    }

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

    @Override public E next() {
        if (!hasNext) throw new NoSuchElementException();
        E out = next;
        iterate();
        return out;
    }

    @Override public void remove() { throw new RuntimeException("N/A"); }
}

And that's the wrapper LazyIterable

public class LazyIterable<E extends Acceptable> implements Iterable<E> {

    private final Iterable<E> wrapped;

    public LazyIterable(Iterable<E> wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public Iterator<E> iterator() {
        return new LazyIterator<E>(wrapped.iterator());
    }
}

Here is the full gist with the test and everything

Upvotes: 1

Eran
Eran

Reputation: 393811

You can use Java 8 Streams:

List<YourClass> list = ...;

Stream<YourClass> filteredList = list.filter(YourClass::acceptable());

Now, only when you run a terminal operation on the Stream, it will consume the source list and find which elements pass the filter.

Upvotes: 1

Related Questions