ArrayList.remove(int)vs ArrayList.remove(Object)在不同的线程中

时间:2013-01-22 20:31:53

标签: java android multithreading list arraylist

我有一个Minion对象的ArrayList,当一个盾与一个minion碰撞时,我想从ArrayList中删除那个minion。但是,我只能让它以单向工作,而不是另一种方式。谁能解释为什么?

在所有3个案例中,我使用Android的Renderer的onDrawFrame()方法...所以我无法控制何时调用它。但这里是所有3种方式的代码:

方法1 :(不起作用)

public void onDrawFrame(GL10 gl) {
    List<Integer> indexesToRemove = new ArrayList<Integer>();
    int len = minions.size();
    for(int i=0; i<len; i++){
        if( OverlapTester.overlapCircleRectangle( (Circle)shield1.bounds,  (Rectangle)minions.get(i).bounds) ){ //this tests out to work just fine
            indexesToRemove.add(i);
        }
    }
    for(int i=indexesToRemove.size()-1; i>=0; i--){
        minions.remove(indexesToRemove.get(i)); //<------ why doesn't this work?
    }
}

问题是最后一行minions.remove(indexesToRemove.get(i));没有实际删除了小兵。它会被适当的索引调用。我已经通过调试器,直接向上运行,并且根本没有修改arraylist。为什么是这样?实际上,在调试器中,该行“minions.remove(indexesToRemove.get(i));”被称为数十亿次。

方法2 :(仍然不起作用)

public void onDrawFrame(GL10 gl) {
    synchronized(minions){
        List<Integer> indexesToRemove = new ArrayList<Integer>();
        int len = minions.size();
        for(int i=0; i<len; i++){
            if( OverlapTester.overlapCircleRectangle( (Circle)shield1.bounds,  (Rectangle)minions.get(i).bounds) ){ //this tests out to work just fine
                indexesToRemove.add(i);
            }
        }
        for(int i=indexesToRemove.size()-1; i>=0; i--){
            minions.remove(indexesToRemove.get(i)); //<------ why doesn't this work?
        }
    }
}

在这里,我想......“哦,也许因为它没有完全同步,drawFrame有时被调用太多次并且在错误的时间访问arraylist并且我需要锁定它。但它仍然没有再次,使用正确的索引正确地调用该行minions.remove(indexesToRemove.get(i));,但实际上并没有删除该对象。我正在看着屏幕上的盾牌直接进入小兵并且没有任何事情发生在小兵身上(它没有不要从arraylist中删除

方法#3(这实际上有效)

public void onDrawFrame(GL10 gl) {
    ArrayList<Minion> colliders = new ArrayList<Minion>(minions);
    int len = colliders.size();
    for(int i=0; i<len; i++){
        GameObject collider = colliders.get(i);
        if(OverlapTester.overlapCircleRectangle((Circle)shield1.bounds, (Rectangle)collider.bounds)){
            minions.remove(collider); // <---- why does THIS work instead?
        }
    }
}

此代码完美无缺。盾牌击碎了仆从,仆从掉落了。正如你在这里看到的,唯一的区别是我正在使用重载的ArrayList.remove(object)方法而不是通过索引删除。如同minions.remove(collider);行。为什么这有效?

任何人都可以解释一下吗?

在旁注上,除了存储arraylist的另一个实例变量副本外,是否有更好的方法来管理ArrayList<Minion> colliders = new ArrayList<Minion>(minions);

注意:Shield和Minion都是具有矩形形状作为边界的常规Java对象。所有数学检查都很好。我在调试器中对它进行了测试,并且碰撞检测是准确的。我也在onDrawFrame()方法更新准确的边界/位置。

3 个答案:

答案 0 :(得分:10)

因为ArrayList提供了两种方法:

public E remove(int index)
public boolean remove(Object o)

当您调用minions.remove(indexesToRemove.get(i))时,由于indexesToRemoveList<Integer>,调用将绑定到第二个签名,您可以通过直接指定对象来删除元素,自动取消装箱不会不要将Integer变成int,因此找不到元素,也没有任何反应。

尝试使用:minions.remove((int)indexesToRemove.get(i)),以便正确应用方法的静态绑定。

答案 1 :(得分:7)

@杰克的回答是正确的。对于后代,你应该在这里使用Iterator,你可以在里面你的循环中删除它:

// synchronization wrapper here
Iterator<Minion> iterator = minions.iterator();
while (iterator.hasNext()) {
    Minion minion = iterator.next();
    if( OverlapTester.overlapCircleRectangle(..., minion.bounds)) {
        iterator.remove();
    }
}

答案 2 :(得分:3)

在前两个示例中将Integer视为对象引用,将其转换为int