views:

223

answers:

4
import java.util.Collection;

import example.Event;

public interface Query
{
    public boolean hasMore ();

    public Collection<Event> getNext ( long count ) throws Exception;
}

This is the interface I have, which I want to implement.

The implementation is supposed to be like this:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import example.Event;
import example.Query;

public class ListQuery implements Query {

    public ListQuery(List<Event> events, String filter)
      throws FilterParseException {
        // events is the list of given events
        // filter is a string representation of the filter to apply
    }

    public Collection<Event> getNext(long count) throws Exception {
         // returns max. count next entries which match given filter
    }

    public boolean hasMore() {
        // returns if there are more elements matching the given filter
    }
}

What I'm thinking about, is the relationship between hasMore() and getNext(). In both cases I have to evaluate if the filter matches an element of the list. Possibly I don't know the implementation of the given list, so it could be an expensive operation. Obviously I cant just use hasNext() from an iterator, because I have to check if the Event matches the given criteria. In my current implementation I have two different iterators and the current position, where the one for hasMore() is moved up if the position of the iterator for getNext() is larger than the one for hasMore().

What I actually would like to do, is to clone the current iterator which I in turn would use for hasMore(), but this is not possible obviously.

Is there a more elegant solution for this problem?

+2  A: 

In your getNext implementation, after you assign your return value, you could advance your iterator until you find an appropriate event. This way, your hasMore can safely test hasNext on your iterator to determine whether to return true or false.

akf
I was busy typing up this exact solution as an example. You can advance the current position as such: `current = null; while (it.hasNext() `
Gunslinger47
@akf - that's not true, strictly speaking. Example: after the call to `getNext()` there's only one element remaining in the list that satisfies the filter and it's the actual last element in the list. You've advanced the iterator position to it; `hasNext()` will return false. You could put it in buffer as suggested by `Gunslinger47` and check that but you'll lose the fail-fast iterator functionality that way
ChssPly76
Yes, I thought of that. If you advance your iterator and pull the items out as you do so, you will iterate *past* your next item, which wouldnt be any good. If you maintain your `List<Event>` and use that to aid in advancing your iterator to the position just before your next good event, it should work.
akf
@akf - by "use that to aid" do you mean via `ListIterator.previous()` or via `List.get()`? Either way could potentially be expensive though I suppose you'd only need to call those for the last element of the list. To be fair, I think the original interface is sub-optimal by itself; considering that `getNext(count)` can return less than `count` at any time, `hasMore()` is rather pointless to begin with. Your solution is likely as good as it'll get within current constraints; +1
ChssPly76
I was thinking in terms of the simple `List.get()`. Thanks.
akf
Somehow I didn't think this far, but thats a simple and for my case a totally appropriate solution.@ChssPly76 Why do you think the interface is sub optimal? In the standard case it is a database operation, the data itself is transmitted over the network via other means. The count option just a way to request a complete block in one request instead of doing a network request for each single entry. In this way the GUI for the user can already display some data even if there is still more to come. My question on the other hand targets a in memory dummy implementation I use for tests.
Mauli
A: 

I am thinking you don't even need two iterators. The hasMore call can iterate all the way until it finds a match and just stay there (if the element that it already points to matches, don't iterate). Now the getNext will use same iterator to iterate and populate return collection until either count is reached or no more matching elements are found.

Rocket Surgeon
A: 

I know in C# this could easily be done with generics and the yield keyword: http://lexparse.com/2009/10/26/c-generics-lambdas-laziness/

Not sure about Java.

MP
+2  A: 

Stop torturing yourself :-) and just use this:

Iterables.filter(Iterable, Predicate)

It takes care of these problems for you.

If you don't have your data in anything Iterable, only an Iterator, see the corresponding class Iterators. If you need to implement the Iterator yourself, it may help to extend AbstractIterator in that same package.

Then if you really want to be able to retrieve chunks of results you can use the partition() method of the Itera*s classes.

Kevin Bourrillion
It looks very much like the easiest solution for my problem.
Mauli