улучшенный итератор коллекции

Лично я нахожу диапазон функций, предоставляемых java.util.Iterator, довольно жалким. Как минимум, я хотел бы иметь такие методы, как:

  • peek() возвращает следующий элемент, не перемещая итератор вперед
  • предыдущий() возвращает предыдущий элемент

Хотя есть много других возможностей, таких как first() и last().

Кто-нибудь знает, существует ли такой сторонний итератор? Вероятно, его нужно будет реализовать как декоратор java.util.Iterator, чтобы он мог работать с существующими коллекциями java. В идеале он должен быть «осведомлен о дженериках».

Заранее спасибо, Дон


person Dónal    schedule 10.10.2008    source источник


Ответы (10)



Вы можете легко получить previous(), просто используя java.util.ListIterator.

Peek в этой точке легко реализовать, выполнив

public <T> T peek(ListIterator<T> iter) throws NoSuchElementException {
    T obj = iter.next();
    iter.previous();
    return obj;
}

К сожалению, будет проще использовать его как служебный метод, поскольку каждый класс коллекции реализует свои собственные итераторы. Чтобы сделать оболочку для получения метода просмотра каждой коллекции на каком-то интерфейсе, таком как MyListIterator, потребовалось бы довольно много работы.

person William    schedule 10.10.2008

Я думаю, что причина, по которой они не реализованы, заключается в том, что они нетривиальны для некоторых коллекций и могут сильно повлиять на производительность. Я думаю, вам было бы довольно просто заставить это работать для коллекций, которые вам небезразличны.

Мне также не нравится, что итераторы Java не могут получить текущее значение, не перемещая его (и, следовательно, вы не можете легко написать код, который разветвляется на основе значения, просто передавая итератор — вы должны передать значение, которое вы теперь тоже есть).

person Lou Franco    schedule 10.10.2008

Есть веская чертова причина, по которой универсальные операторы не реализуют эти функции: они существуют не для всех контейнеров. Типичным примером является контейнер, представляющий некоторые внешние входные данные, такие как файл, рассматриваемый как поток. Каждый раз, когда вы читаете значение, вы используете его и перемещаете указатель вперед, хотите вы этого или нет. Если вы наложите эти ограничения на универсальные итераторы, вы потеряете универсальность итераторов.

Если вам нужен метод previous, как было предложено, используйте ListIterator<>, который затем ограничивается контейнером, ведущим себя как список.

person PierreBdR    schedule 10.10.2008
comment
Аргумент файла не очень убедителен, нет причин, по которым мы не могли бы получить текущий символ (или что-то, что мы читаем) без перемещения курсора в файле. Однако я согласен с тем, что функция previous не может быть предоставлена ​​каждым итератором (особенно итераторами-генераторами, которые генерируют новое значение на каждом шаге). - person Luc Touraille; 30.06.2011
comment
@Luc: это потому, что вы видите файлы как произвольный доступ. И они действительно не должны быть. Если вы предпочитаете, рассмотрите поток, такой как сетевые сокеты. Вы читаете то, что получаете, и если вы хотите читать вперед, вам нужен целый механизм кэширования. Поэтому, если вы хотите, чтобы ваш итератор использовался во всех ситуациях, вам действительно нужен интерфейс, который предоставляет Java. После этого вы можете использовать адаптеры или более специализированные итераторы. Остается, что интерфейс, предоставляемый Java, является наиболее разумным для универсальных итераторов. - person PierreBdR; 01.07.2011
comment
Это не вопрос произвольного доступа: даже в прямом потоке я не вижу причины, по которой доступ к только что прочитанному значению и чтение следующего следует рассматривать как одну операцию. Но я предполагаю, что это довольно субъективно, и мы должны согласиться с несогласием :). Тем не менее, я хотел бы добавить, что мне кажется немного странным иметь метод remove в интерфейсе, который должен быть очень универсальным, когда операция удаления возможна только для довольно ограниченного набора итерируемых объектов. - person Luc Touraille; 01.07.2011

Одна вещь, на которую я бы обратил внимание, это реализация Seq в clojure.

http://clojure.org/sequences

Реализация базовых классов находится на Java, и доступен полный исходный код. Seqs — это декораторы на java-итераторах (взять и реализовать интерфейсы java-итераторов), но они также предоставляют свой собственный интерфейс, который может быть больше того, что вы хотите, или, по крайней мере, отправной точкой.

person Lou Franco    schedule 10.10.2008

Я видел, что кто-то связался с Google Collections, но никто не упомянул, что метод, который вы ищете, называется Iterators.peekingIterator().

Тем не менее, было бы лучше, если бы вы могли просто использовать ListIterator.

person Kevin Bourrillion    schedule 04.11.2009

Как предложил ykaganovich, вы можете проверить материалы google-collections. Определенно существует некоторая поддержка некоторых вещей, которые вам нужны, например просмотр. Кроме того, как уже упоминалось некоторыми другими, реализация всех этих вещей для всех коллекций может быть опасной с точки зрения возможности или производительности.

person Paul Wicks    schedule 10.10.2008

Я никогда не сталкивался с проблемой, когда мне нужно было заглянуть(); Итератор отлично сработал для меня. Мне любопытно, как вы используете итераторы, которые, по вашему мнению, нуждаются в этой дополнительной функциональности.

person Steve g    schedule 10.10.2008
comment
Я думаю, самый распространенный случай — это какая-то рассылка. Вы хотите прочитать первый элемент, чтобы увидеть, кто должен его обработать и отправить — если получателю нужна вся последовательность, лучше просто передать ее, а не передавать удаленный объект. - person Lou Franco; 10.10.2008
comment
Я это вижу. Это просто пахнет чем-то, что можно было бы сделать намного проще каким-то другим способом, например, посетителем или чем-то в этом роде. - person Steve g; 10.10.2008

Похоже, вам лучше использовать стек.

person Draemon    schedule 10.10.2008

Коллекции Java были написаны для предоставления минимального набора полезных функций. Это очень хороший подход к коду, который должен реализовать любой, кто внедряет Java. Раздувание интерфейса функциональностью, которая может быть полезной, может привести к значительному увеличению объема кода, а улучшения заметят лишь немногие. Если peek() и previous() были частью стандартного итератора, это означает, что каждый, кто пишет новый вид Collection, должен реализовать его, независимо от того, разумно это или нет.

Итераторы также предназначены для работы с вещами, которые физически не могут вернуться назад, что делает невозможными функции peek() и previous().

person DJClayworth    schedule 18.11.2008