Реализация Java Iterator - следующий () и hasNext () принудительный порядок - PullRequest
8 голосов
/ 01 февраля 2010

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

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

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

Ответы [ 8 ]

18 голосов
/ 01 февраля 2010

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

15 голосов
/ 01 февраля 2010

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

7 голосов
/ 01 февраля 2010

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

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:

документ для hasNext() говорит:

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

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

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

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

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

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

4 голосов
/ 01 февраля 2010

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

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

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

2 голосов
/ 10 февраля 2010

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

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

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

2 голосов
/ 01 февраля 2010

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

1 голос
/ 01 февраля 2010

Вы можете сделать что-то похожее на приведенное ниже, в котором вы делегируете базовую выборку данных частному методу и реализуете 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();
    }
  }
}
0 голосов
/ 26 апреля 2016

РЕДАКТИРОВАТЬ: В этом ответе я пытался утверждать, что это разрешено, о чем спрашивает вопрос. Но я пропустил одно предложение в Iterator.hasNext документации, которое лишает законной силы все мои рассуждения:

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

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

Таким образом, кажется, что вопрос о том, что не разрешено.

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

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

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

Обсуждение

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

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

А для Iterator.next:

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

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

Это приводит к вопросу: что конкретно означает документация с «итерацией» и «элементами»? Документация Iterator не дает ответа на этот вопрос, что дает некоторую возможность для разработчиков.

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

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

    Следовательно, согласно спецификации для разработчика итератора можно решить, что итерация завершена. Так что ответ на вопрос - да.

  2. Но в документации по Iterable.iterator также упоминаются "элементы":

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

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

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

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

Conclution

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

Примечание

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

Например, в документации ArrayList.iterator четко указано , на каких элементах закончился итератор:

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


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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...