将 ArrayList 嵌入式循环转换为迭代器

Posted

技术标签:

【中文标题】将 ArrayList 嵌入式循环转换为迭代器【英文标题】:Converting ArrayList embedded looping to Iterator 【发布时间】: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,这可能不像看起来那么简单,因为两个循环遍历同一个列表并可能相互干扰。那么问题来了,我该如何解决这个问题呢?

【问题讨论】:

【参考方案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)。

以上是关于将 ArrayList 嵌入式循环转换为迭代器的主要内容,如果未能解决你的问题,请参考以下文章

Java - 迭代 ArrayList 并查找 Anagrams

不能转换迭代器对象

Iterator迭代例题

ArrayList中Iterator的研究学习

为每个循环迭代 ArrayList 获取 a 的当前索引

迭代器