Как лучше всего использовать какой-то ленивый итератор, когда возврат оценивается только по запросу?

import java.util.Collection;

import example.Event;

public interface Query
{
    public boolean hasMore ();

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

Это интерфейс, который у меня есть, который я хочу реализовать.

Реализация должна быть такой:

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
    }
}

Я думаю о связи между hasMore() и getNext(). В обоих случаях я должен оценить, соответствует ли фильтр элементу списка. Возможно, я не знаю реализации данного списка, поэтому это может быть дорогостоящей операцией. Очевидно, я не могу просто использовать hasNext() из итератора, потому что мне нужно проверить, соответствует ли событие заданным критериям. В моей текущей реализации у меня есть два разных итератора и текущая позиция, где позиция для hasMore() перемещается вверх, если позиция итератора для getNext() больше, чем позиция для hasMore().

Что я на самом деле хотел бы сделать, так это клонировать текущий итератор, который я, в свою очередь, использовал бы для hasMore(), но это, очевидно, невозможно.

Есть ли более элегантное решение этой проблемы?


person Mauli    schedule 27.10.2009    source источник


Ответы (3)


В вашей реализации getNext после присвоения возвращаемого значения вы можете продвигать свой итератор до тех пор, пока не найдете подходящее событие. Таким образом, ваш hasMore может безопасно протестировать hasNext на вашем итераторе, чтобы определить, следует ли возвращать true или false.

person akf    schedule 27.10.2009
comment
Я был занят вводом этого точного решения в качестве примера. Вы можете продвинуть текущую позицию как таковую: current = null; while (it.hasNext() && !passesFilter(current = it.next())); - person Gunslinger47; 27.10.2009
comment
@akf - это, строго говоря, неправда. Пример: после вызова getNext() в списке остался только один элемент, удовлетворяющий фильтру, и это фактически последний элемент в списке. Вы продвинули к нему позицию итератора; hasNext() вернет false. Вы можете поместить его в буфер, как было предложено Gunslinger47, и проверить это, но таким образом вы потеряете функциональность отказоустойчивого итератора. - person ChssPly76; 27.10.2009
comment
Да, я думал об этом. Если вы продвинете свой итератор и вытащите элементы, когда вы это сделаете, вы будете итерировать прошлый ваш следующий элемент, что было бы нехорошо. Если вы поддерживаете свой List<Event> и используете его, чтобы помочь продвинуть свой итератор на позицию непосредственно перед вашим следующим хорошим событием, это должно сработать. - person akf; 27.10.2009
comment
@akf - под помощью, которую вы имеете в виду, через ListIterator.previous() или через List.get()? В любом случае потенциально может быть дорого, хотя я полагаю, что вам нужно будет вызывать их только для последнего элемента списка. Честно говоря, я думаю, что исходный интерфейс сам по себе неоптимален; учитывая, что getNext(count) может вернуть меньше count в любое время, hasMore() для начала довольно бессмысленно. Ваше решение, вероятно, настолько хорошо, насколько оно будет соответствовать текущим ограничениям; +1 - person ChssPly76; 27.10.2009
comment
Я думал о простом List.get(). Спасибо. - person akf; 27.10.2009
comment
Как-то я не думал так далеко, но это простое и для моего случая вполне подходящее решение. @ChssPly76 Как вы думаете, почему интерфейс не оптимален? В стандартном случае это работа с базой данных, сами данные передаются по сети другими способами. Опция count просто способ запросить полный блок в одном запросе вместо того, чтобы выполнять сетевой запрос для каждой отдельной записи. Таким образом, графический интерфейс для пользователя уже может отображать некоторые данные, даже если это еще не все. С другой стороны, мой вопрос нацелен на фиктивную реализацию в памяти, которую я использую для тестов. - person Mauli; 28.10.2009

Перестаньте мучить себя :-) и просто используйте это:

Iterables.filter(Iterable, Predicate)

Он позаботится об этих проблемах за вас.

Если у вас нет данных ни в чем Iterable, только в Iterator, см. соответствующий класс Iterators. Если вам нужно реализовать итератор самостоятельно, может помочь расширение AbstractIterator в том же пакете.

Затем, если вы действительно хотите получить фрагменты результатов, вы можете использовать метод partition() классов Itera*s.

person Kevin Bourrillion    schedule 04.11.2009
comment
Это очень похоже на самое простое решение моей проблемы. - person Mauli; 04.11.2009
comment
За исключением, конечно, того, что любой, кто занимается этой проблемой в наши дни, должен использовать code.google.com/p/guava. -libraries, который является преемником библиотеки коллекций Google. - person Daniel Martin; 08.11.2011

Я думаю, вам даже не нужны два итератора. Вызов hasMore может повторяться до тех пор, пока не найдет совпадение, и просто оставаться там (если элемент, на который он уже указывает, соответствует, не повторять). Теперь getNext будет использовать тот же итератор для итерации и заполнения возвращаемой коллекции до тех пор, пока не будет достигнуто количество или не будут найдены соответствующие элементы.

person Rocket Surgeon    schedule 27.10.2009