Почему Java Iterator и ListIterator указывают между элементами?

9

Javadoc для ListIterator говорит:

А ListIteratorне имеет текущего элемента; его позиция курсора всегда находится между элементом, который будет возвращен вызовом, previous()и элементом, который будет возвращен вызовом next().

Почему в Java ListIteratorреализовано указание между элементами, а не на текущий элемент? Кажется , что это делает код клиента менее читаемым , когда он должен повторно звонить getNext(), getPrevious(), поэтому я полагаю , что должно быть веские основания для выбора.

В качестве примечания, я просто написал мало - библиотека под названием peekable-ArrayList , который проходит ArrayList, Iteratorи ListIteratorчто обеспечивает peekAtNext()и peekAtPrevious()методы реализованы как:

  @Override public synchronized T peekAtNext() {
     T t = next();
     previous();
     return t;
  }
glenviewjeff
источник
2
Существует peekable итератор в библиотеке гуавы code.google.com/p/guava-libraries
Кевин Клайн
Спасибо, Кевин! Я написал следующий вопрос на SO: возможно ли использовать ForwardingListIterator в Guava с PeekingIterator?
glenviewjeff

Ответы:

12

Насколько я могу судить, причина может быть найдена в той части Javadoc, которую вы не цитировали (выделено ниже моей):

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

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

Теперь подумайте, что произойдет, если мы удалим элемент, который был бы current()для итератора - предполагая, что итератор будет иметь представление о текущем элементе? В этом контексте способ реализовать его без представления о текущем элементе имеет для меня довольно хороший смысл - потому что в этом случае итератору не нужно беспокоиться об удалении элементов.


Это важно отметить , что Javadoc не требует реализации интерфейса потокобезопасной.

  • Из-за этого не следует ожидать правильной обработки изменений, выполненных из разных потоков - для этого реализация должна будет предоставить дополнительные средства для синхронизации доступа, гарантии видимости и т. Д., Как указано в модели памяти Java для JSR 133 .

То, на что способен ListIterator, обрабатывает изменения, сделанные из одного и того же потока при итерации. Не все итераторы такие, Javadocs ConcurrentModificationException специально предупреждают об этом:

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

комар
источник
1
Это также означает, что вам не нужно отдельное insertBeforeи insertAfter, хотя это не такая большая проблема, как remove.
Карл Билефельдт
1
Так может ли проблема быть одновременно удалена и получить доступ к текущему элементу? Разве это не было бы эквивалентной проблемой как одновременный вызов next()? remove()будет synchronizedкак бы getCurrent(). Я что-то пропустил?
glenviewjeff
@glenviewjeff это очень хорошее наблюдение. Мне также интересно, как CME мог быть обработан там - само заявленное намерение разрешить модификации вызывает подобные проблемы. Думаю, мне нужно копать глубже. Я на 99,99% уверен, что он не предназначен для работы с потоками, может быть, он может обрабатывать только параллельные моды, сделанные из одного потока (знаете, не все итераторы способны на это)
gnat
Если вам интересно, посмотрите мои правки. Я реализовал и упаковал расширение ArrayListдля достижения этой цели.
glenviewjeff
@glenviewjeff интересно. В вашей реализации синхронизируются ли next () и previous ()? Я спрашиваю, потому что если нет, то другой поток может «сверлить» неожиданное движение прямо в середину вашего current (), заставляя его вести себя не совсем так, как ожидал бы клиент ... Методы, такие как add (), вероятно, тоже нуждаются в синхронизации
комнат