迭代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.shipsProjectile.projectilesList 对象并且将使用这些对象而不是它们的迭代器:

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时从数组中删除对象的主要内容,如果未能解决你的问题,请参考以下文章

迭代时从 HashSet 中删除元素 [重复]

迭代时从集合中删除元素

Java 小程序在从迭代器数组中删除对象时抛出错误

如何在迭代时从地图中删除?

如何在迭代时从 HashMap 中删除键? [复制]

Python:如何在迭代列表时从列表中删除元素而不跳过未来的迭代