我需要通过“瓷砖”的arraylist进行检查,如果找到一个未使用的瓷砖,则在该瓷砖周围添加4个瓷砖,但是当我在检查列表时添加新内容时,它会崩溃。如何在检查时将对象添加到arraylist而不会崩溃?
我添加一个磁贴,然后检查它:
private static List<Tile> tiles = new ArrayList<Tile>();
tiles.add(new Tile(mapsize/2*tilesize, mapsize/2*tilesize));
for(Tile tile: tiles){
if(!tile.spread){
tile.spread=true;
tiles.add(new Tile(tile.position.x-tilesize, tile.position.y)); //this line crashes it
}
}
答案 0 :(得分:3)
迭代列表并同时修改它会导致 ConcurrentModificationException 。为了避免这种情况,您可以将新项目添加到新列表中,当您完成遍历原始列表时 - 将两个列表合并到原始列表中。例如:
private static List<Tile> tiles = new ArrayList<Tile>();
private static List<Tile> tmpTiles = new ArrayList<Tile>();
tiles.add(new Tile(mapsize/2*tilesize, mapsize/2*tilesize));
for(Tile tile: tiles){
if(!tile.spread){
tile.spread=true;
tmpTiles.add(new Tile(tile.position.x-tilesize, tile.position.y));
}
}
tiles.addAll(tmpTiles);
答案 1 :(得分:1)
如果您想要另一种解决问题的方法,可以使用Java 8流并按照以下方式实现:
List<String> strings = Arrays.asList("10", "20", "30");
final List<String> result = strings.stream().collect(ArrayList::new, (c, s) -> {
if (s.equals("20")) {
c.add("15"); // Here the result collection is modified
c.add(s);
c.add("25");
} else {
c.add(s);
}
}, ArrayList::addAll);
System.out.println(result); // -> [10, 15, 20, 25, 30]
答案 2 :(得分:0)
当我在检查列表时添加新内容时,它会崩溃
ConcurrentModificationException
。这是预料之中的。除了使用Iterator
&#39; .remove()
之外,你不能像你那样修改列表(不是像ArrayList
这样的非并发列表)。
您应该创建一个新列表,在其中添加新的图块,然后当您完成迭代时,将所有这个新列表添加到现有列表中:
final List<Tile> newTiles = new ArrayList<>();
for (final Tile tile: tiles) {
if (tile.spread)
continue;
tile.spread = true;
newTiles.add(new Tile(tile.position.x - tilesize, tile.position.y));
}
tiles.addAll(newTiles);
当然,上面的代码假设您的代码不是同时从多个线程调用的!或者此代码位于受锁定保护的方法中......
答案 3 :(得分:0)
使用并发集合或锁定。我可以建议CopyOnWriteArrayList甚至更好的CopyOnWriteArraySet。
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArrayList.html
编辑:以下是使用可重入锁定的示例
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); // estimated number of entries needed should be set on creation, 25 in this ex private static ArrayList<Tile> tiles = new ArrayList<Tile>(25); public boolean add(Tile tile) { boolean added = false; // get read lock r.lock(); // do the tiles list checks and set a flag to add or not for (Tile t: tiles) { if (t.spread) { continue; } // flag the new tile tile.spread = true; break; } // release read lock r.unlock(); // get write lock w.lock(); try { // add the tile added = tiles.add(tile); // add the four "surrounding" tiles here? } finally { // release write lock w.unlock(); } return added; } public void clear() { w.lock(); try { tiles.clear(); } finally { w.unlock(); } }
答案 4 :(得分:0)
一个ConcurrentModificationException是错误,一些集合旨在阻止你这样做,因为如果它被允许它可能会导致意外的输出并且可能是不安全的,你刚刚添加的项也会被迭代
与其他人一样,最好将新项目添加到单独的列表中,并使用内置的集合方法在最后组合列表。
允许(尽管不明智)在给定集合大小的情况下迭代带有for循环的列表。例如:
ArrayList<String> arrlist = new ArrayList<String>();
arrlist.add("foo");
arrlist.add("bar");
arrlist.add("foo");
arrlist.add("bar");
arrlist.add("foo");
for(int i = 0; i < arrlist.size(); i++){
if(arrlist.get(i).equals("foo")){
arrlist.add("bar");
}
System.out.println(arrlist.get(i));
}
输出也会在最后一个foo之后打印3个条形,因为当元素为foo时添加的项目也会在上面的实例中被迭代,这样会很好但不好练习。
但是ConcurrentModificationExceptions确实存在,并且使用上面的代码是不好的做法。