Использование ListIterator для перемещения вперед и назад по LinkedList в Java

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

LinkedList navigationCases;
navigationCases.add("page1");
navigationCases.add("page2");
navigationCases.add("page3");
navigationCases.add("page4");

ListIterator navigationItr = navigationCases.listIterator();
navigationItr.next(); // Returns page1
navigationItr.next(); // Returns page2
navigationItr.previous(); //Returns page2 again
navigationItr.next(); //Returns page2 again

Я подумал, что, возможно, я неправильно строю свой список или неправильно использую итератор, но после прочтения документации это выглядит так:

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

А также:

(Next) Возвращает следующий элемент в списке. Этот метод может вызываться многократно для итераций по списку или смешиваться с вызовами к предыдущим для перехода вперед и назад. (Обратите внимание, что чередующиеся вызовы next и previous будут возвращать один и тот же элемент несколько раз.)

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

Обратите внимание, что методы remove () и set (Object) не определены в терминах позиции курсора; они определены для работы с последним элементом, возвращаемым вызовом next () или previous ().

Концептуально, LinkedList, казалось, довольно хорошо моделировал мои рабочие процессы, но я могуt использовать Итератор, который ведет себя таким образом. Я что-то здесь упускаю, или я должен просто написать свой собственный класс, вести список дел и перемещаться по ним?

 Kurt Koller20 нояб. 2012 г., 23:34
@ Madbreaks Вы должны вызывать предыдущий только дважды, когда предыдущая операция была следующей (). В противном случае вы звоните только один раз. Отсюда и мое разочарование :)
 Kurt Koller20 нояб. 2012 г., 23:19
@JBNizet Это имеет смысл. Я, по крайней мере, могу понять, почему это будет реализовано таким образом, но я думаю, что не смогу использовать LinkedList для этой проблемы. Или, по крайней мере, яМне нужно написать свой собственный ListIterator.
 JB Nizet20 нояб. 2012 г., 23:13
Он ведет себя так, потому что делает итерацию и удаление, начиная с конца, точно так же, как итерацию и удаление, начиная с начала.
 Madbreaks20 нояб. 2012 г., 23:23
Если нужно, оберните основные функции в свою собственную логику, которая вызываетiterator.previous().previous() (очевидно, псевдо), когда вы проситеmyIterator.previous()
 Madbreaks20 нояб. 2012 г., 23:10
Почему можнот "использовать итератор, который ведет себя так? Остальной мир делает. Просто примите, как это работает, и используйте это так, как это было задумано.
 Kurt Koller20 нояб. 2012 г., 23:15
@Madbreaks Чем этот вариант использования отличается от того, каким он был "предназначен для использования " затем? Как мне учесть "специальный" случай запроса предыдущего элемента сразу после запроса следующего? Или просить следующий сразу после того, как просил предыдущий?
 Avik25 сент. 2014 г., 18:35
Это серьезно расстраивает поведение. У меня ушло несколько часов, чтобы воспроизвести и устранить проблему, содержащую этот код.
 Paul Lammertsma18 дек. 2017 г., 13:46
Это плохо документированное поведение, и оно явно связано с плохим решением по разработке API. Если итератор неt явно указывают на один элемент, также не должно бытьremove() метод как это нене различать лиследующий' или же 'предыдущая элемент должен быть удален. Это действительно должно было быть разработано с помощью методов, называемыхremoveNext() а такжеremovePrevious() так что поведениеnext() а такжеprevious() будет как ожидалось. Я рекомендую вообще не использовать ListIterator и вместо этого писать простой итератор вручную.

Ответы на вопрос(3)

Сделайте что-то вроде этого (псевдокод) -

class SkipIterator extends ListIterator {

    public E previous(){
        E n = super.previous();
        return super.previous();
    }

    ...

}

затем:

LinkedList<string> navigationCases;
navigationCases.add("page1");
navigationCases.add("page2");
navigationCases.add("page3");
navigationCases.add("page4");

SkipIterator navigationItr = (SkipIterator)navigationCases.listIterator();
navigationItr.next(); // Returns page1
navigationItr.next(); // Returns page2
navigationItr.previous(); // Returns page1
</string>

ура

 Kurt Koller20 нояб. 2012 г., 23:51
Да ты прав. Спасибо!
 Madbreaks11 июл. 2013 г., 01:09
Даунвотер заговори!
 Madbreaks20 нояб. 2012 г., 23:47
Итак, встроите эту логику. :) Идея состоит в том, что вы можете расширить базовый класс и включить логику, необходимую для вашего варианта использования.
 Kurt Koller20 нояб. 2012 г., 23:33
К сожалению, я хочу вызывать previous () только дважды, если предыдущая операция была следующей (). Если next не было вызвано ранее, то, если дважды вызвать previous (), фактически итератор пропустит элемент в списке.
 Paul Lammertsma18 дек. 2017 г., 13:48
проведениеprevious() дважды для каждого вызова не даст желаемого поведения при переходе назад без предварительного вызова.next()

чтобы вести себя таким образом. Смотрите разговор под ShyJ 'ответ за обоснование.

Я считаю, что это поведение выходит за рамки идиотизма, и вместо этого я написал очень простую альтернативу. Вот's код Котлина с функцией расширения для ArrayLists:

class ListIterator<e>(var list: ArrayList<e>) : Iterator<e> {

    private var cursor: Int = 0

    fun replace(newList: ArrayList<e>) {
        list = newList
        cursor = 0
    }

    override fun hasNext(): Boolean {
        return cursor + 1 < list.size
    }

    override fun next(): E {
        cursor++
        return current()
    }

    fun hasPrevious(): Boolean {
        return 0 <= cursor - 1
    }

    fun previous(): E {
        cursor--
        return current()
    }

    fun current(): E {
        return list[cursor]
    }

}

fun <e> ArrayList<e>.listFlippingIterator() = ListIterator(this)
</e></e></e></e></e></e>

Если вы хотите включить функциональность удаления, я настоятельно рекомендую написать API, чтобы явно указать итератору, следует ли ему удалять левое или правое, например, определив эти методы какremoveNext() а также .removePrevious()

Решение Вопроса

Это должно сделать вашу работу:

public class Main {
    public static void main(String[] args) {
        final LinkedList<string> list = new LinkedList<string> ();

        list.add ("1"); list.add ("2"); list.add ("3"); list.add ("4");

        final MyIterator<string> it = new MyIterator (list.listIterator());

        System.out.println(it.next());
        System.out.println(it.next ());
        System.out.println(it.next ());
        System.out.println(it.previous ());
        System.out.println(it.previous ());
        System.out.println(it.next ());
    }

    public static class MyIterator<t> {

        private final ListIterator<t> listIterator;

        private boolean nextWasCalled = false;
        private boolean previousWasCalled = false;

        public MyIterator(ListIterator<t> listIterator) {
            this.listIterator = listIterator;
        }

        public T next() {
            nextWasCalled = true;
            if (previousWasCalled) {
                previousWasCalled = false;
                listIterator.next ();
            }
            return listIterator.next ();
        }

        public T previous() {
            if (nextWasCalled) {
                listIterator.previous();
                nextWasCalled = false;
            }
            previousWasCalled = true;
            return listIterator.previous();
        }

    }   
}
</t></t></t></string></string></string>

Ииграть на скрипке для этого.

 Kurt Koller20 нояб. 2012 г., 23:48
Да я неЯ не вижу необходимости вспоминать предыдущую операцию. Спасибо!

Ваш ответ на вопрос