如何循环在循环期间更改的ArrayList

时间:2015-05-27 16:33:51

标签: java arraylist

我有一个Sprite类:

public class Sprite {
    public static ArrayList<Sprite> sprites = null;
    private Texture texture = null;

    public Sprite(Texture texture) {
        sprites.add(this);
        this.texture = texture;
    }

    public void render(SpriteBatch batch) {
        batch.draw(texture);
    }

    public void dispose() {
        sprites.remove(this);
    }
}

在创建任何精灵之前,我确保Sprite类引用了将要渲染的精灵池:

public class Main extends ApplicationAdapter {
    private static ArrayList<Sprite> sprites = new ArrayList<Sprite>();

    public Main() {
        Sprite.sprites = sprites;

        new Sprite("texture.png");
    }

    @Override
    public void render(SpriteBatch batch) {
        for (int i = 0; i < sprites.size(); i++)
            sprites.get(i).render(batch);
    }
}

所以现在当我创建一个新的精灵时,它会自动渲染,而不必手动将它添加到ArrayList中。

问题是sprite可以在for循环运行期间处理,因此不会为该特定帧渲染一些sprite。

我不希望向后循环这些项目,例如:

for (int i = sprites.size() - 1; i >= 0; i--)
    sprites.get(i).render(batch);

因为这会使我的精灵失灵(应该在顶部的精灵将被覆盖)。

到目前为止,我唯一的解决方案是使用另一个ArrayList来跟踪要处理的对象,如下所示:

public class Sprite {
    public static ArrayList<Sprite> sprites = null, garbage = null;

    //everything else the same

    public void dispose() {
        garbage.add(this);
    }
}

然后在渲染过程中,我首先从sprite中删除所有垃圾,然后渲染精灵:

for (int i = 0; i < garbage.size(); i++)
    sprites.remove(garbage.get(i));
garbage.clear();

for (int i = 0; i < sprites.size(); i++)
            sprites.get(i).render(batch);

这种方法有效,但我觉得它效率低下。 (有两个arraylists?然后做两个for循环?)

有没有什么方法可以循环Sprite,而不会在Sprite处理时跳过Sprite?我不想向后循环Sprites,因为它会搞乱订单。

我现在使用同步列表测试了这个,但我无法正常工作。

Testable code,(java.util.ConcurrentModificationException

import java.util.*;

class Ideone {

    public static class Sprite {
        public static List<Sprite> sprites = null;
        public int age = 0;

        public Sprite() {
            synchronized(sprites) {
                sprites.add(this);
            }
        }

        public void render() {
            age++;
            if (age > 30)
                dispose();
        }

        public void dispose() {
            synchronized(sprites) {
                sprites.remove(this);
            }
        }
    }

    private static List<Sprite> sprites = Collections.synchronizedList(new ArrayList<Sprite>());

    public static void main(String[] args) {
        Sprite.sprites = sprites;

        new Sprite();
        new Sprite();

        for (int i = 0; i < 60; i++)
            render();

    }

    public static void render() {
        synchronized(sprites) {
            Iterator<Sprite> iterator = sprites.iterator();
            while (iterator.hasNext())
                iterator.next().render();
        }
    }
}

3 个答案:

答案 0 :(得分:2)

如果您只需要处理remove,请使用Iterator之类的

Iterator<Sprite> itr = sprites.iterator();
while (itr.hasNext()) {
  // do something with itr.next()
  // call itr.remove() to remove the current element in the loop
}

Iterator提供remove方法从列表中删除任何项目,但请确保不向列表中添加任何新项目,否则会抛出ConcurrentModificationException

答案 1 :(得分:2)

首先,如果从多个线程访问sprites列表,则需要同步对它的访问或使用线程安全数据结构(例如CopyOnWriteArrayList或CopyOnWriteArraySet)。

在这种情况下,

Sprite.dispose可以直接从sprites中删除。

答案 2 :(得分:0)

这对我有用clicky

import java.util.*;

class Ideone {

    public static class Sprite {
        public static ArrayList<Sprite> sprites = null;
        public int age = 0;

        private boolean removed = false;

        public Sprite() {
            sprites.add(this);
        }

        public boolean render() {
            if (removed)
                return false;
            age++;
            if (age > 30)
                dispose();
            return true;
        }

        public void dispose() {
            removed = true;
        }
    }

    private static ArrayList<Sprite> sprites = new ArrayList<Sprite>();

    public static void main(String[] args) {
        Sprite.sprites = sprites;

        new Sprite();
        new Sprite();

        for (int i = 0; i < 60; i++)
            render();

    }

    public static void render() {
        Iterator<Sprite> iterator = sprites.iterator();
        while (iterator.hasNext())
            if (!iterator.next().render())
                iterator.remove();
    }
}

所以,当应该删除一个精灵时,我设置一个快速布尔值,告诉主渲染循环用迭代器删除它。我将不得不调整它以使用同步列表(这样添加项目不会引发异常)。