你好,我正在尝试像贪吃蛇游戏一样在玩家后面添加尾巴。但是由于某种原因,这一直滞后于我的游戏并耗尽内存。为什么会发生这种情况,我该如何解决这个问题?
我像这样创建列表:
List<Snake> snake = new CopyOnWriteArrayList<Snake>();
这是我创建新对象并在 forloop 中删除它们的地方:
public void snake() {
snake.add(new Snake(ball.getX(), ball.getY()));
currentTime++;
for(Snake currentSnake: snake) {
if(currentSnake.creationDate < SnakeLength){
currentSnake.Update();
} else {
Gdx.app.log("SnakeLength" + SnakeLength, "CreationDate" + currentSnake.creationDate);
snake.remove(currentSnake);
}
}
}
这是我的蛇类的样子:
public class Snake
{
float width = Gdx.graphics.getWidth();
float height = Gdx.graphics.getHeight();
float screenwidth = width/270;
float screenheight = height/480;
public float x;
public float y;
public int creationDate;
ShapeRenderer shapeRenderer;
SpriteBatch batch;
public boolean active = false;
public Snake(float x, float y) {
shapeRenderer = new ShapeRenderer();
batch = new SpriteBatch();
this.x = x;
this.y = y;
}
public void Update() {
batch.begin();
shapeRenderer.begin(ShapeType.Filled);
shapeRenderer.setColor(new Color(1, 1, 1, 0.2f));
shapeRenderer.circle(x + 8*screenwidth, y + 8*screenheight, 6*screenheight);
shapeRenderer.end();
batch.end();
creationDate++;
}
}
正如javadoc CopyOnWriteArrayList所述:
ArrayList 的线程安全变体,其中所有可变操作(添加、设置等)都是通过创建基础数组的新副本来实现的。
因此,这是昂贵的操作,并且此类非常适合修改率较小的并发读取。看起来这不是你的情况。
如果您选择此类来解决有关在修改列表时读取列表的问题,请考虑将迭代器与 ArrayList 一起使用,这些迭代器专为该用途而设计:
synchronized(snake) { // Protect the snake list against concurrent access. Do it whenever you access the snake list
for(Iterator<Snake> it = snake.iterator();; it.hasNext()) {
Snake currentSnake = it.next();
if(currentSnake.creationDate < SnakeLength) {
currentSnake.Update();
} else {
Gdx.app.log("SnakeLength" + SnakeLength, "CreationDate" + currentSnake.creationDate);
it.remove();
}
}
}
请注意,ArrayList 不受并发访问的保护。如果多个线程能够读取和写入列表,则必须使用同步块保护它。
但是阅读您的代码,我想带有PriorityQueue的设计似乎更合适。
我基本上认为你会得到java.util.ConcurrentModificationException
例外,因为你试图删除元素(Snakes
),同时迭代你的List
,如@Dragondraikk所述。
这不是使用CopyOnWriteArrayList
的好理由,因为您可以在此处阅读例如输入链接描述,它解释了CopyOnWriteArrayList
的有用性。基本上CopyOnWriteArrayList
是有用的,如果
你最需要迭代 ArrayList,不要太频繁地修改它
你没有。
你为什么不在检索元素后修改你拥有的列表。如果您对这种行为不满意,即您没有恒定数量的元素(我猜您没有),请尝试在单独的循环中更新它们,而不是删除/添加它们。
来回答OPs的问题:
你要去OOM,因为你正在使用CopyOnWriteArrayList
,我引用官方文档是
ArrayList 的线程安全变体,其中所有可变操作(添加、设置等)都是通过创建基础数组的新副本来实现的。 (着重号后加)
循环访问项目时,无法从列表中删除该项目。在更新其余的蛇之前,您需要删除需要删除的蛇。
我建议:
snakes.removeIf(snake -> snake.createDate > snakeLength);
snakes.stream().forEach(Snake::Update);