Реализация итератора Java-next() и hasNext () обеспечение порядка
у меня есть реализация java.util.Iterator
который требует, чтобы вызов next()
всегда должен быть продолжен вызовом hasNext()
. (Это связано с тем, что результаты возвращаются асинхронно в многопоточной среде, и никогда не ясно, сколько еще результатов может быть).
было бы "правильно" правильно документировать это в JavaDoc, а затем бросить RuntimeException
если это было нарушено. Или это слишком растягивает интерфейс итератора?
все мысли оценены?
8 ответов
требуя, чтобы 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, если итерация содержит больше элементов.
Броски:
NoSuchElementException
- если итерация не имеет больше элементов
по-видимому, это разрешено бросать NoSuchElementException
когда "итерация больше никаких элементов", но не раньше. Это должно совпадать с when hasNext
возвращает false.
это приводит к вопросу: что именно означает документация с "итерацией" и "элементами"? The Iterator
документация не дает ответа на этот вопрос,что дает некоторое пространство для разработчиков.
на мой взгляд есть две возможные интерпретации:
-
С точки зрения самого интерфейса итератора только существующая концепция "итерации" - это " до тех пор, пока
hasNext
возвращает значение true". Это означает, что если клиент называетnext
доhasNext
они не знают, есть ли больше элементов, это неопределенно.это, следовательно, разрешено спецификацией для реализации итератора, чтобы решить, что итерация закончилась. Поэтому ответ на вопрос-да.
-
но документация на
Iterable.iterator
отметить "elements":возвращает итератор над элементами типа
T
.так что же здесь означает "элементы"? Означает ли это "все элементы в коллекции, реализующие
Iterable
? Нет, он так не говорит, и не все итераторы даже есть фиксированный набор элементов.что означают" элементы " для какой-то конкретной итерации, остается решать разработчику. Допустимым определением "элементов" для итератора может быть "все элементы в коллекции, или, все элементы, прежде чем клиент решит позвонить
next
доhasNext
".таким образом, этот случай также приводит к выводу, что ответ на вопрос-да. (Но см. Примечание В конце!)
заключение
документация не совсем ясна, но кажется, что ответ на вопрос: Да, это разрешено.
Примечание
In конечно, если итератор делает то, что задает вопрос об этом поведении, должно быть задокументировано. Но другие итераторы должны также документ, над которым элементы их итераторы.
например,ArrayList.iterator
в документации четко указано , который элементы итератор над:
возвращает iterator по элементам в этом списке в правильной последовательности.
последнее замечание: да, я сумасшедший, чтобы тратить так много времени на это.