将 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<T>
实现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 < 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 嵌入式循环转换为迭代器的主要内容,如果未能解决你的问题,请参考以下文章