Реализация итератора Java-next() и hasNext () обеспечение порядка

у меня есть реализация java.util.Iterator который требует, чтобы вызов next() всегда должен быть продолжен вызовом hasNext(). (Это связано с тем, что результаты возвращаются асинхронно в многопоточной среде, и никогда не ясно, сколько еще результатов может быть).

было бы "правильно" правильно документировать это в JavaDoc, а затем бросить RuntimeException если это было нарушено. Или это слишком растягивает интерфейс итератора?

все мысли оценены?

8 ответов


возможно, я что-то пропустил здесь, но почему бы не позвонить hasNext() внутри вашей реализации?


требуя, чтобы hasNext() перед next() нарушение iterator контракт. Вы действительно должны переписать его так, что next() просто бросает!--4--> если нет элемента для возврата.


Я представляю, что вы делаете что-то вроде этого:

class IteratorImpl<T> implements Iterator<T> {
  private Source<T> source = ...
  private T next = null;

  public boolean hasNext() {
    if(next == null) {
      next = source.poll();
    }
    return next != null;
  }

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


EDIT:

на doc на hasNext() говорит:

возвращает true, если итерация содержит больше элементов. (Другими словами, возвращает true, если next возвращает элемент, а не бросает исключение.)

для меня реализация не нарушает договор. Однако я бы (как Фабиан Стиг подразумевает) все-таки реализовать next() as:

  public T next() {
    if(!hasNext()) {
      throw new NoSuchElementException();
    }
    T ret = next;
    next = null;
    return ret;
  }

Я имею в виду, что этот чек действительно стоит вам?

вы должны проверить и кинуть NoSuchElementException на контракт API. Либо тестирование на !hasNext() или next == null будет соответствовать этим критериям, я считаю, но я бы предпочел первое.

если кто-то ловит NoSuchElementException вместо hasNext(), у вас, вероятно, есть большие проблемы.


если hasNext() и next() вызовы не находятся в синхронизированном блоке / методе, не гарантируется, что у вас будут элементы, даже если вы вызываете hasNext() до next().

контракт Iterator интерфейс это NoSuchElementException должен быть брошен, если больше нет элементов. Так что продолжайте next() метод, пока не возникнет такое исключение.

что сказал, взгляните на java.util.concurrent package - он имеет параллельные коллекции, итераторы которых может помочь вам, т. е. вы можете использовать эти коллекции и итераторы вместо реализации собственных.


Я бы предпочел выбросить исключение из next(), когда больше нет элементов. В многопоточной среде hasNext() в любом случае это довольно бесполезно.


что вы можете сделать-это реализовать Iterator интерфейс самостоятельно (если вам нужно взаимодействовать с другим API) и попросите своих клиентов реализовать свой собственный более строгий интерфейс.

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

мой класс EasierIterator реализует интерфейс итератора, требуя, чтобы клиент реализовал более простой интерфейс на основе moveNext()/getCurrent(). Он фактически выполняет кэширование текущего элемента для вас.


вы можете сделать что-то подобное приведенному ниже, в результате чего вы делегируете базовую выборку данных частному методу и реализуете hasNext() и next() по-разному реагировать на отсутствие данных. Это имеет то преимущество, что вы можете многократно называют next() без вызова hasNext() во-первых, и, следовательно,не нарушайте договор итератора.

public class IteratorImpl<T> implements Iterator<T> {
  private final Source<T> source;
  private T next;

  public synchronized boolean hasNext() {
    tryGetNext();
    return next != null;
  }

  public synchronized T next() {
    tryGetNext();

    if (next == null) {
      throw new NoSuchElementException();
    } 

    return next;
  }

  private void tryGetNext() {
    if (next != null) {
      next = source.poll();
    }
  }
}

EDIT: в этом ответе я попытался возразить, что разрешено то, о чем спрашивает вопрос. Но я пропустил одну фразу в Iterator.hasNext документация, которая аннулирует все мои рассуждения:

другими словами, возвращает true if next() вернет элемент, а не вызовет исключение.

это, кажется, означает, что вызов next до hasNext возвращает true и вызывает next, пока вы не получите NoSuchElementException должна возвращать ту же последовательность элементов.

таким образом, кажется, что вопрос задает не допущено.

оригинальный ответ

это попытка по спецификации юриста типа ответа. Для ясности я переформулирую вопрос в компактной форме:

не допускается Iterable спецификация бросить NoSuchElementException, когда Iterator.next вызывается без предшествующего вызова Iterator.hasNext, даже если элемент было бы возвращениеIterator.hasNext называют первым?

Обсуждение

документация Iterator.hasNext гласит:

возвращает true, если итерация содержит больше элементов.

и Iterator.next:

Броски: NoSuchElementException - если итерация не имеет больше элементов

по-видимому, это разрешено бросать NoSuchElementException когда "итерация больше никаких элементов", но не раньше. Это должно совпадать с when hasNext возвращает false.

это приводит к вопросу: что именно означает документация с "итерацией" и "элементами"? The Iterator документация не дает ответа на этот вопрос,что дает некоторое пространство для разработчиков.

на мой взгляд есть две возможные интерпретации:

  1. С точки зрения самого интерфейса итератора только существующая концепция "итерации" - это " до тех пор, пока hasNext возвращает значение true". Это означает, что если клиент называет next до hasNext они не знают, есть ли больше элементов, это неопределенно.

    это, следовательно, разрешено спецификацией для реализации итератора, чтобы решить, что итерация закончилась. Поэтому ответ на вопрос-да.

  2. но документация на Iterable.iterator отметить "elements":

    возвращает итератор над элементами типа T.

    так что же здесь означает "элементы"? Означает ли это "все элементы в коллекции, реализующие Iterable? Нет, он так не говорит, и не все итераторы даже есть фиксированный набор элементов.

    что означают" элементы " для какой-то конкретной итерации, остается решать разработчику. Допустимым определением "элементов" для итератора может быть "все элементы в коллекции, или, все элементы, прежде чем клиент решит позвонить next до hasNext".

    таким образом, этот случай также приводит к выводу, что ответ на вопрос-да. (Но см. Примечание В конце!)

заключение

документация не совсем ясна, но кажется, что ответ на вопрос: Да, это разрешено.

Примечание

In конечно, если итератор делает то, что задает вопрос об этом поведении, должно быть задокументировано. Но другие итераторы должны также документ, над которым элементы их итераторы.

например,ArrayList.iterator в документации четко указано , который элементы итератор над:

возвращает iterator по элементам в этом списке в правильной последовательности.


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