我有一个与GalaxyInvaders克隆相关的问题。
for
和foreach
迭代ArrayList
之间有什么区别?
我正在使用ArrayLists
循环修改foreach
(更改变量/删除某些对象)(我在使用Semaphores
执行此操作之前阻止了各个部分)。
问题是,当我使用foreach
语句时,应用程序抛出异常。
Exception in thread "Thread-39" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at pl.mosek.MyPanel2$ColissionDetector.run(GUI.java:421)
at java.lang.Thread.run(Unknown Source)
当我使用经典for
语句时,问题会神奇地消失。
有人可以解释我为什么会这样吗?
CODE:
@Override
public void run() {
while (true) {
if (ship.getBullets().isEmpty() == false
&& monsters.isEmpty() == false) {
semaphoreLock(semaphoreBullet);
for (Bullet i : ship.getBullets()) {
if (i.isDestroyed() != true) {
semaphoreLock(semaphoreMonster);
for (Enemy j : monsters) {
if (i.getBounds()
.intersects(j.getBounds())
&&j.isDestroyed() != true) {
i.setDestroyed(true);
i.setVisible(false);
j.setDestroyed(true);
j.setVisible(false);
}
}
}
semaphoreRelease(semaphoreMonster);
}
semaphoreRelease(semaphoreBullet);
}
try {
Thread.sleep(25);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
如果我将foreach
切换为for
个,那么一切正常。
答案 0 :(得分:3)
使用标准for循环,您可以通过get()
访问列表中的元素,并(可能)通过set()
设置它们。这些方法无法检测并发修改。但是,对于for-each循环,您实际上使用的是Iterator
方法返回的iterator()
does check for concurrent modification。因此存在差异。
for-each循环
for (Element e : list) {
...
}
实际上变成了:
for (Iterator<Element> iter = list.iterator(); iter.hasNext();) {
Element e = iter.next();
...
}
答案 1 :(得分:1)
虽然有些existing answers可能会解释您可以做些什么来使您的代码正常运行,但他们无法准确解释出现了什么问题以及原因。
特别是,foreach
语法通过ArrayList#iterator()
使用迭代器,docs for ArrayList说:
此类的
iterator
和listIterator
方法返回的迭代器是 fail-fast :如果在创建迭代器后的任何时候对列表进行结构修改,则任何除了通过迭代器自己的remove
或add
方法之外,迭代器将抛出ConcurrentModificationException
。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒着任意的,非确定性行为的风险。