在检查时添加一些东西给arraylist

时间:2014-12-20 21:44:52

标签: java arraylist

我需要通过“瓷砖”的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
        }
    }

5 个答案:

答案 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确实存在,并且使用上面的代码是不好的做法。