【问题标题】:Converting ArrayList embedded looping to Iterator将 ArrayList 嵌入式循环转换为迭代器
【发布时间】:2016-03-28 15:32:13
【问题描述】:

我在获取使用迭代器背后的逻辑时遇到问题。我需要能够从循环内的 ArrayList 中删除元素,所以我想我会使用 Iterator 对象。但是,我不确定如何将原始代码转换为迭代器。

原始循环:

ArrayList<Entity> actorsOnLocation = loc.getActors();
int size = actorsOnLocation.size();

if (size > 1) {
    for(Entity actor: actorsOnLocation) {
        // Loop through remaining items (with index greater than current)
        for(int nextEnt=index+1; nextEnt < size-1; nextEnt++) {
            Entity opponent = actorsOnLocation.get(nextEnt);
            // Here it's possible that actor or opponent "dies" and
            // should be removed from the list that's being looped
        }
    }
}

我知道我必须使用 while 循环,它适用于第一个循环。但是如何将第二个循环的条件转换为与迭代器一起使用的条件? This post 表示您可以在任何时候获取迭代器,但在 the documentation 我找不到诸如 .get 方法之类的任何东西。

ArrayList<Entity> actorsOnLocation = loc.getActors();
int size = actorsOnLocation.size();
Iterator actorsIterator = actorsOnLocation.iterator();

// How to get size of an iterator?
if (size > 1) {
    while(actorsIterator.hasNext()) {
        Entity actor = (Entity) actorsIterator.next();
        // How to get current index?
        int index = actorsIterator.indexOf(actor);
        // How to convert these conditions to Iterator?
        for(int nextEnt=index+1; nextEnt < size-1; nextEnt++) {
            Entity opponent = actorsOnLocation.get(nextEnt);
            // Here it's possible that actor or opponent "dies" and
            // should be removed from the list that's being looped

            // If the actor dies, the outer loop should skip to the next element
        }
    }
}

最后一个问题:如果您在迭代器中访问一个元素,该元素不是原始元素的副本,对吗?换句话说,我可以为该元素设置属性,然后通过访问原始 Collection 来访问这些更改的属性?

作为Eran pointed out,这可能不像看起来那么简单,因为两个循环遍历同一个列表并可能相互干扰。那么问题来了,我该如何解决这个问题呢?

【问题讨论】:

    标签: java loops arraylist iterator


    【解决方案1】:

    演员或对手有可能“死亡”并应从列表中删除

    你可以通过ListIterator&lt;T&gt;实现actor的移除,但是移除对手是非法的,因为actor的迭代器会失效。这会导致ConcurrentModificationException

    你需要改变你的算法:因为你需要从两个位置移除,你需要将内部循环而不是外部循环转换为使用迭代器:

    outerLoop:
    for(int i = 0 ; i < actorsOnLocation.length()-1 ; i++) {
        Entity actor = actorsOnLocation.get(i);
        // Loop through remaining items (with index greater than current)
        ListIterator<Entity> oppIter = actorsOnLocation.listIterator(i+1);
        while (oppIter.hasNext()) {
            Entity opponent = oppIter.next();
            // Here it's possible that actor or opponent "dies" and
            // should be removed from the list that's being looped
            if (opponent.mustDie()) {
                oppIter.remove();
            } else if (actor.mustDie()) {
                // The following operation invalidates oppIter
                actorsOnLocation.remove(i);
                // so we must continue the outer loop
                continue outerLoop;
            }
        }
    }
    

    【讨论】:

    • 未测试,但第一个循环不应该是i &lt; actorsOnLocation.size() - 1,因为你总是需要能够为对手获取actor的索引+1? (size() 因为它是一个 ArrayList。)我现在才看到这个,但这也是我自己的代码中的一个错误。
    • 第二,如果对手死了,它并没有从actorsOnLocation中删除,所以它仍然是外循环中的下一个项目,对吗?也许解决这个问题的最好方法是给演员添加一个布尔值,这样当他们死时它会返回假,然后在循环开始时检查他们是否“活着”,如果不是continue;?你怎么看?
    • remove() 在属于列表的列表迭代器上被调用时,@BramVanroy 对手从actorsOnLocation 中移除。
    • 哦,如果我理解正确的话:迭代器基本上是原始集合的影子副本,它会传递对自身所做的更改?另外,为什么要删除整个迭代器?不应该只移除对手,因为现在行中的下一个对象不再循环了吗?
    • @BramVanroy 迭代器不是副本,它是原始集合的通用索引。对于数组列表,它本质上是一个美化的int:当你调用oppIter.next() 时,它会在它维护的int 上执行++,并返回list.get(internal_index)。当您执行oppIter.remove(); 时,它会调用list.remove(internal_index),依此类推。迭代器相对于int 索引和ArrayList 的优势纯粹是美学上的。然而,一旦你切换到LinkedList,你会获得巨大的性能提升,因为索引到链表的开销是 O(n)。
    猜你喜欢
    • 2019-11-10
    • 2013-10-20
    • 2016-02-16
    • 2015-01-08
    • 2017-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多