如何有效地删除ArrayList元素?

时间:2017-07-25 05:07:26

标签: java arraylist game-engine

在我的游戏引擎中,有粒子,所有这些粒子的寿命都略有不同。每次更新,它们的生命都会缩短,当它达到0时,它将从ArrayList中删除,这意味着它将不再被更新或渲染。虽然在正常的游戏过程中我没有遇到这种方法的问题,但是当我有大量的粒子(大约100,000)时,在创建和删除(但不更新)粒子时存在很大的滞后。

由于这种滞后,我希望提高此设置的效率。我已经尝试过只删除这么多的每次更新,剩下的用于下次更新但是没有提供太多的结果。

package com.burksnet.code.games.rain.entity.particle;

import com.burksnet.code.games.rain.entity.Entity;
import com.burksnet.code.games.rain.graphics.Screen;
import com.burksnet.code.games.rain.graphics.Sprite;
import com.burksnet.code.games.rain.level.Level;

public class Particle extends Entity {

    private Sprite sprite;
    private int life;

    private double speed = 2;

    public static int maxParticleCount = 2500000;

    protected double xa, ya;
    //Life is how long it lives, x, y, and level are obvious.
    public Particle(int x, int y, int life, Level level) {
        super(level);
        //The sprite to be rendered.
        sprite = Sprite.particleNormal;
        this.x = x;
        this.y = y;
        this.life = life;

        //how it will move. effectively randomly.
        this.xa = random.nextGaussian() /2 * speed;
        this.ya = random.nextGaussian() /2 * speed;

    }

    //Renders the particle on screen.
    public void render(Screen screen) {
        if(life <= 0 ) return;
        screen.renderSprite(x, y, sprite, true);
    }

    //Updates it.
    public void update() {
        if(life <= 0 ){
            //Removes the item from the level, making it no longer be rendered or updated. See the method below.
            level.remove(this);
        }
        this.x += xa;
        this.y += ya;
        life--;
    }

}

Level.remove(粒子e)低于

public void remove(Particle e) {
    particles.remove(e);
}

Level.add(粒子e)如下。在创建粒子时使用,因此可以更新和渲染粒子。

public void add(Particle e) {
    particles.add(e);
}

我想这绝对有可能这只是一个无法克服的纯粹限制,但我希望事实并非如此。

4 个答案:

答案 0 :(得分:1)

让我们来看看从ArrayList中间删除项目时会发生什么。

       +---+---+---+---+---+
Start  | 1 | 2 | 3 | 4 | 5 |
       +---+---+---+---+---+
Step 1 | 1 | 2 |   | 4 | 5 |
       +---+---+---+---+---+
Step 2 | 1 | 2 | 4 |   | 5 |
       +---+---+---+---+---+
Step 3 | 1 | 2 | 4 | 5 |   |
       +---+---+---+---+---+

正如您所看到的,花费的大部分时间不是删除项目本身,而是将之后的项目移动到新的位置。这就是为什么,对于100,000个记录的列表,删除将花费很长时间。

如果您不需要随机访问(使用get(i)),那么您可以考虑使用LinkedList。如果你确实需要get(i),那么你将需要做出一些艰难的决定。

答案 1 :(得分:0)

问题似乎在于您迭代和更新粒子的方式。您需要使用LinkedList并在Level中对其进行迭代。我建议重构update()方法,以便在boolean从关卡中移除时返回Entity信号。

public class Particle {
    public boolean update() {
        if (life <= 0) {
            return false; // remove me!
        }
        this.x += xa;
        this.y += ya;
        life--;
        return true; // update successful
    }
}

public class Level {
    public boolean update() {
        Iterator<Particle> it = particles.iterator();
        while (it.hasNext()) {
            Particle p = it.next();
            if (!p.update()) {
                it.remove(); // Remove is O(1) with LinkedList
            }
        }
        return true; // update successful
    }
}

答案 2 :(得分:0)

我猜有几件事情会导致你所看到的滞后。

数据结构的大小

每个double占用8个字节的内存。你的坐标和速度真的需要64位浮点值吗?

我不知道您的Entity类中有什么,但是快速计算导致我(保守地)猜测每个Particle对象将占用至少32个字节的内存。对于100,000个粒子,这将是大约3 MB的堆。如果在游戏过程中经常删除粒子对象,那么JVM的垃圾收集器可能会给延迟带来很大的影响。

您使用浮点数学

计算xa和ya坐标时使用

考虑使用整数数学,因为这可以为游戏的性能提供显着的改进。

在ArrayList中添加和删除

正如您所指出的,添加和删除ArrayList可能很昂贵。对于add,有可能必须重新分配内存以容纳新添加的元素。对于删除,删除后的所有元素都必须复制回列表中的一个位置。

如果您选择使用ArrayList,您可以做的一件事就是构建一个初始容量接近您认为的最大粒子数的容量。这具有立即保留大量堆的缺点,但是将避免经常重新分配。 ArrayList的默认初始容量为10.

您可能会考虑使用不同的List实现,以便为添加/删除提供更好的性能。 LinkedList符合法案,可能是像TreeMap一样有序的Map。

通过分析来收集有关您应用的可靠数据

您可以做的最好的事情之一是在应用程序运行时分析您的应用程序,以查看CPU周期的使用情况以及堆分配。如果可以提供帮助,请不要预先优化。首先测量!祝你好运!

答案 3 :(得分:-1)

class Array {
  public Static void main(string args[]) {
    ArrayList<String> lst=new ArrayList<String>();
    lst.add("abc");
    lst.add("xyz");
    lst.add("pqr");
    Iterator<String> itr=lst.Iterator();
    while(itr.hasNext()) {
      String ele=itr.next();
      if(ele.equals("xyz") {
        itr.remove();
      }
      System.out.println(ele);  
    }
  }
}