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

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

LinkedList<String> 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

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

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

и

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

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

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

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

Ответы

Ответ 1

Это должно выполнить вашу работу:

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

    }   
}

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

Ответ 2

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

Я считаю, что это поведение не должно быть идиотским, и вместо этого написало очень простую альтернативу. Здесь код Котлина с функцией расширения для 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)

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

Ответ 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

Приветствия