使用ListIterator在Java中的LinkedList上来回移动

2022-09-02 04:43:23

我有一个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

我想也许我错误地构建了我的列表,或者错误地使用了迭代器,但是在阅读了文档之后,这似乎是设计使然:

ListIterator 没有当前元素;它的游标位置始终位于调用 previous() 将返回的元素和由调用 next() 返回的元素之间。

和:

(下一页)返回列表中的下一个元素。可以重复调用此方法以循环访问列表,也可以混合使用对 previous 的调用来回访问。(请注意,交替调用下一个和上一个将重复返回相同的元素。

因此,在阅读本文后,很明显为什么我的代码会以这种方式运行。我只是不明白为什么它应该以这种方式工作。甚至删除似乎也在向后弯曲以适应此实现:

请注意,remove() 和 set(Object) 方法不是根据游标位置定义的;它们被定义为对通过调用 next() 或 previous() 返回的最后一个元素进行操作。

从概念上讲,LinkedList似乎很好地模拟了我的工作流案例,但我不能使用以这种方式运行的迭代器。我在这里遗漏了什么,还是我应该编写自己的类来维护一个案例列表并浏览它们?


答案 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答案下面的对话以了解基本原理。

我发现这种行为是愚蠢的,而是写了一个非常简单的替代方案。下面是带有 ArrayList 扩展函数的 Kotlin 代码:

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