迭代Java时从数组中删除对象
Posted
技术标签:
【中文标题】迭代Java时从数组中删除对象【英文标题】:Deleting objects from arrays while iterating Java 【发布时间】:2017-11-12 12:00:40 【问题描述】:我正试图在它们碰撞时同时删除一艘船和一个射弹。 为此,我将遍历两个高级 for 循环,并尝试在它们相交时删除它们。不幸的是,在迭代它们的同时编辑 for 循环并不是一个好主意,它会引发 ConcurrentModificationException,所以我将它们换成 Iterators,这似乎是有效的。
public void collision()
Iterator<Ship> itShips = Ship.ships.iterator();
Iterator<Projectile> itProj = Projectile.projectiles.iterator();
while (itShips.hasNext())
Ship ship = itShips.next();
while (itProj.hasNext())
Projectile proj = itProj.next();
if (ship.c != proj.c)
Rectangle.Float r1 = ship.getBounds();
Rectangle.Float r2 = proj.getBounds();
if (r1.intersects(r2))
itProj.remove();
itShips.remove();
break;
问题在于 ConcurrentModificationException 似乎已移至我调用更新程序的位置。我也尝试将这些 for 循环换成迭代器,但它似乎不起作用并抛出相同的异常,但现在在 update() 方法中。
public void update()
Iterator<Ship> it1 = Ship.ships.iterator();
while (it1.hasNext())
Ship s = it1.next();
s.update(game);
Iterator<Projectile> it2 = Projectile.projectiles.iterator();
while (it2.hasNext())
Projectile p = it2.next();
p.update(game);
我应该更改更新游戏对象的方式还是保存它们的方式?还是我以错误的方式删除对象?
【问题讨论】:
【参考方案1】:您可以将那些发生碰撞的变量保存到某个变量中,然后在完成循环后,将它们从列表中删除:
Ship shipToRemove = null;
Projectile projToRemove = null;
Iterator<Ship> itShips = Ship.ships.iterator();
Iterator<Projectile> itProj = Projectile.projectiles.iterator();
while (itShips.hasNext())
Ship ship = itShips.next();
while (itProj.hasNext())
Projectile proj = itProj.next();
if (ship.c != proj.c)
Rectangle.Float r1 = ship.getBounds();
Rectangle.Float r2 = proj.getBounds();
if (r1.intersects(r2))
shipToRemove = ship;
projToRemove = proj;
break;
Projectile.projectiles.remove(projToRemove);
Ship.ships.remove(shipToRemove);
应该怎么做。
【讨论】:
这仍然给我的更新方法一个错误。但是,在我以这种方式迭代后,我也许可以使用它来删除它们。 @Muxor 如果您发现一个有用的答案,您应该点赞,以便未来的读者知道它很有用。 对于有同样问题的人:我使用了这个想法,但是因为它在 update() 方法中仍然给我一个错误,所以我不得不稍微改变它。我没有直接删除 collideObjects,而是将它们存储在一个数组中。在我的更新方法中,我放置了一些代码来从存储实际对象的数组中删除存储的碰撞对象。【参考方案2】:迭代器不支持在迭代时添加。在集合迭代器中,使用 expectedModCount 来检查它是否被其他修改。当您使用 set reference modCount 值增加而进行一些添加时,expectedModCount 未更改会导致异常。
您应该在最后跟踪并执行更新操作。
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
public interface Iterator
boolean hasNext();
E next();
void remove();
【讨论】:
【参考方案3】:解决问题的一种实用方法是filter
非碰撞的船只和射弹。这种方法避免了同时改变多个对象所产生的问题。我假设 Ship.ships
和 Projectile.projectiles
是 List
对象并且将使用这些对象而不是它们的迭代器:
List<Ship> ships = Ship.ships;
List<Projectile> projs = Projectile.projectiles;
Stream<Rectangle.Float> shipBounds = ships.stream().map(s -> s.getBounds());
Stream<Rectangle.Float> projBounds = projs.stream().map(p -> p.getBounds());
List<Ship> safeShips = ships
.stream()
.filter(s -> !projBounds.anyMatch(p -> p.intersects(s.getBounds())))
.collect(Collectors.toList());
List<Projectile> safeProjs = projs
.stream()
.filter(p -> !shipBounds.anyMatch(s -> s.intersects(p.getBounds())))
.collect(Collectors.toList());
【讨论】:
【参考方案4】:由于它在我的 update() 方法中仍然出错,我将碰撞对象存储在数组中。使用这些数组,我可以从原始 Ship 和 Projectile 数组中删除它们。为此,我在 update() 方法中调用另一个方法来比较和删除对象。
public void collision()
Ship shipToRemove = null;
Projectile projToRemove = null;
outerLoop:
for (Ship ship : Ship.ships)
for (Projectile proj : Projectile.projectiles)
if (ship.c != proj.c)
Rectangle.Float r1 = ship.getBounds();
Rectangle.Float r2 = proj.getBounds();
if (r1.intersects(r2))
shipToRemove = ship;
projToRemove = proj;
playSound();
break outerLoop;
projALToRemove.add(projToRemove);
shipALToRemove.add(shipToRemove);
public void update()
for (Ship ship : Ship.ships)
ship.update(game);
for (Projectile proj : Projectile.projectiles)
proj.update(game);
deleteAfterCollision();
public void deleteAfterCollision()
for (Ship ship : Ship.shipALToRemove)
Ship.ships.remove(ship);
for (Projectile proj : Ship.projALToRemove)
Projectile.projectiles.remove(proj);
【讨论】:
以上是关于迭代Java时从数组中删除对象的主要内容,如果未能解决你的问题,请参考以下文章