Реализация Итератора Java - затем () и hasNext () осуществляющий порядок

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

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

Все мысли ценятся?

8
задан Joachim Sauer 1 February 2010 в 12:03
поделиться

7 ответов

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

18
ответ дан 5 December 2019 в 04:35
поделиться

Мне кажется, ты делаешь что-то вроде этого:

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

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


EDIT:

В doc для hasNext() написано:

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

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

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

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

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

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

7
ответ дан 5 December 2019 в 04:35
поделиться

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

Если вы используете LinqToSql или какой-либо другой ИЛИ Mapper, то это, скорее всего, само по себе реализует образец UnitOfWork, поэтому часто мы просто используем экземпляр ORMapper в нашем собственном IUnitOfWork.

Наш интерфейс репозитория, как правило, что-то вроде..

  IEnumerable<Order> FindByCustomerId(string customerId);
  void Add(Order order);
  void Remove(Order order);

У нас нет метода сохранения в репозитории. Если UnitOfWork не требуется, методы Add/Remove действуют непосредственно в хранилище данных.

Если нам нужен UnitOfWork, то общедоступный интерфейс будет чем-то вроде...

void Commit();
void Rollback();

Репозиторий имеет внутренний интерфейс с UnitOfWork, поэтому при запросе репозитория возвращенные объекты отслеживаются UnitOfWork на предмет изменений. Метод фиксации записывает изменения обратно в хранилище данных, метод отката просто очищает их.

Когда мы используем LinqToSql, DataContext заботится об отслеживании изменений, при откате мы просто создаем экземпляр нового контекста. Стойкость обрабатывается в корне и его потомках. Один экземпляр UnitOfWork является общим для всех репозиториев.

Когда мы не используем LinqToSql, тогда мы реализуем наш собственный UnitOfWork, возможно, он вызывает веб-службу или что-то подобное, в этом случае мы изменяем отслеживание в самих классах сущностей, используя класс EntityBase.

У нас есть репозиторий для каждого корня, но иногда потомки одного корня используются в качестве самих корней, поэтому нам часто нужно что-то вроде OrderLineRepository, потому что у нас есть пример использования в нашей системе, если пользователь хочет искать строки заказа.

-121--2225243-

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

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

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

-121--3429427-

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

2
ответ дан 5 December 2019 в 04:35
поделиться

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

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

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

4
ответ дан 5 December 2019 в 04:35
поделиться

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

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();
    }
  }
}
1
ответ дан 5 December 2019 в 04:35
поделиться

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

15
ответ дан 5 December 2019 в 04:35
поделиться

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

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

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

2
ответ дан 5 December 2019 в 04:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: