【问题标题】:Clone an Iterator in Java?在 Java 中克隆一个迭代器?
【发布时间】:2011-10-13 17:45:45
【问题描述】:

在一个游戏中,我有一个玩家列表,可以这样说:

LinkedList<String> players = new LinkedList<String>();

我想让每个玩家与其他玩家互动,所以我写了两个嵌套循环:

Iterator<String> i1 = players.iterator();
while (i1.hasNext()) {
    String p1 = i1.next();
    Iterator<String> i2 = players.iterator();
    // But I want to do this: Iterator<String> i2 = i1.clone();
    while (i2.hasNext()) {
        String p2 = i2.next();
        System.out.println("Interact: " + p1 + ", " + p2);
    }
}

由于我只希望每对玩家互动一次,我想在外循环的当前玩家之后与玩家开始内循环。所以我想克隆迭代器,但是编译不了。

那么,我应该怎么做呢?

【问题讨论】:

  • 为什么不直接使用ArrayList&lt;String&gt;呢?使用该数据结构的位置算法将是微不足道的。
  • 这不会导致玩家 A 与 A 配对吗?
  • @Kirk:是的,ArrayList 可以,但由于我可能需要在列表中间插入和删除玩家,我想要一个 LinkedList。
  • @matt b:是的。谢谢你。固定(我希望)。 (当我无法测试我的代码时会发生这种情况。它总是错!)
  • @Thomas,只是通常情况下,重新洗牌比使用LinkedList 更快,因为实例化列表元素条目的成本通常比简单地移动内存要高。跨度>

标签: java iterator


【解决方案1】:

下面会做:

ListIterator<String> i1 = players.listIterator(0);
while (i1.hasNext()) {
    String p1 = i1.next();
    ListIterator<String> i2 = players.listIterator(i1.nextIndex());
    while (i2.hasNext()) {
        String p2 = i2.next();
        System.out.println("Interact: " + p1 + ", " + p2);
    }
}

它依赖于ListIterator 从给定位置开始并知道其当前位置的能力。

【讨论】:

  • aix 再次出击。 +1,漂亮的解决方案。
  • 谢谢!但是,有一个问题:listIterator(int) 方法是如何工作的 - 它不是从列表的头部开始并迭代到给定的位置,对吗?
  • @ThomasPadron-McCarthy:这没有被指定为界面的一部分。但是,鉴于链表的性质,几乎可以肯定。另一方面,我不知道有任何解决方案可以避免这种情况。
  • @aix,对您的评论 +1。但是,有一个明显的解决方案,如果 OP 愿意从 O(1)O(n) 内存消耗,请参阅我的答案。
  • 我最终将 LinkedList 更改为 ArrayList。在其他地方可能会慢一些,但至少我不会遇到不必要的遍历(主要是审美)问题。但我仍然不明白为什么他们决定不让你克隆迭代器。我什至考虑过使用带有可克隆迭代器的专用列表,但我会等到我知道我需要性能。
【解决方案2】:

除了aix answer,我想指出的是,无论您如何创建一个从特定索引开始的迭代器,它都必然是一个线性操作。如果不是,您将能够使用

在恒定时间内对列表进行任意访问
elementN = createIterator(linkedList, N).next();

这将是矛盾的。

因此,在您的情况下,我认为最有效的解决方案实际上是这样做

List<String> tmp = new ArrayList<String>(players);
for (int p1 = 0; p1 < tmp.size(); p1++)
    for (int p2 = p1+1; p2 < tmp.size(); p2++)
        System.out.println("Interact: " + tmp.get(p1) + ", " + tmp.get(p2));

但是请注意,它仍然与 aix 的解决方案一样复杂; O(n2) 但可能具有较小的常数因子。

【讨论】:

  • 我不同意。如果你有一个迭代器指向你想要的索引,只要接口允许,你应该能够立即到达那个索引。这个假想的i1.clone() 方法是O(1),而players.listIterator(i1.nextIndex())O(n)clone() 方法将为克隆的迭代器提供它当前持有的节点,从而无需迭代到列表中的那个点。
  • 原则上你是对的。想象中的克隆方法实现起来很简单。然而,在这种情况下,OP 很可能指的是不支持克隆的 java.util.LinkedList。关于这个主题的许多问题。参见例如stackoverflow.com/questions/5963399/…
【解决方案3】:

有关避免与listIterator(int)(NPE 的回答)相关的线性成本的解决方案,请参阅我的answer to a similar question。 简而言之,只要不关心列表被访问的顺序,就可以从最后一个元素开始外循环并往回迭代,从第一个元素开始内循环并向前迭代,直到两个迭代器相遇.对 list.listIterator(list.size()) 的调用很快,因为 list 是一个 LinkedList,即双向链表,并且访问最后一个元素不需要遍历列表。请参见下面的示例:

public static int iterRevIterator(List<Integer> list) {
    int sum = 0;
    for(ListIterator<Integer> outer = list.listIterator(list.size()); outer.hasPrevious(); ) {
        Integer oVal = outer.previous();
        for(ListIterator<Integer> inner = list.listIterator(); inner.nextIndex() <= outer.previousIndex(); ) {
            sum += oVal * inner.next();
        }
    }
    return sum;
}

【讨论】:

    猜你喜欢
    • 2018-03-07
    • 2011-07-10
    • 2015-11-23
    • 2017-03-26
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 2020-10-19
    • 1970-01-01
    相关资源
    最近更新 更多