Java:如何在迭代/添加元素时从列表中删除元素

时间:2009-03-09 15:22:57

标签: java algorithm collections list

这个问题是this question中描述(和解决)问题的一个更特殊的例子。

我有两个方法,stopAndRemove(ServerObject服务器)和close()方法。后者应关闭所有服务器并将其从服务器列表中删除。该列表定义为

List<ServerObject> server. 

我不想在closeCurrentlyOpen中使用与stopAndRemove几乎相同的代码,所以我想做类似的事情:

public void closeCurrentlyOpen() {
   for(ServerObject server : this.servers) {
       stopAndRemove(server)
   }
}

这不起作用,因为这会导致ConcurrentModificationException。我试着复制一份清单

List<ServerObject> copyList = new ArrayList<ServerObject>(this.servers);

并将其用作foreach循环的列表。但是当我在copyList上迭代时,另一个线程可能会将服务器附加到服务器列表,但closeCurrentlyOpen应该会产生一个emtpy列表。 当addServerToList方法同步到servers-list时,执行此操作

public void closeCurrentlyOpen() {
   synchronized(this.servers) {
     for(ServerObject server : this.servers) {
        stopAndRemove(server)
     }
    }
}

将通过修改解决问题。但是,我无法同步stopAndRemove方法中的代码,如果直接调用它是必需的。

在我看来,这三种方法的设计可能需要修改。 任何人的想法?

9 个答案:

答案 0 :(得分:17)

从stopAndRemove()中分离出一个方法stop()。然后使用显式迭代器编写循环,执行stop,然后使用iterator.remove()。

方法名称中的

“和”是代码气味。

答案 1 :(得分:14)

这可能是错误的方法,但我总是创建一个删除集合,其中包含对需要删除的对象的索引或引用。然后我遍历该集合并从原始集合中删除这些索引/对象。可能不是最有效的,但它完成了工作。

而不是

for(Collection things : thing)  
    things.remove(thing)

我用

Collection toRemove = new LinkedList();
for(things : thing)
    toRemove.add(thing);

for(toRemove : thing)
    things.remove(thing)

答案 2 :(得分:4)

当我之前完成此操作时,我总是使用“旧学校”LinkedList集合,Iterator和Iterator.remove()方法来删除当前项目。

答案 3 :(得分:3)

您可能会发现this article about ConcurrentModificationException在这方面有一些建议。

答案 4 :(得分:2)

将所有ServerObject停止代码从stopAndRemove重构为私有stopServer方法,然后在stopAndRemove和closeCurrentlyOpen中单独执行删除操作。然后你可以使用ListIterator来删除它们(或者只是在for循环中将它们全部停止并在结尾处清除列表)。

答案 5 :(得分:1)

你应该得到一个迭代器并使用它删除。您将获得异常,因为迭代器在java中是fail-fast

答案 6 :(得分:1)

回答问题的标题,而不是给定示例的具体细节。事实上,这种解决方案在特定情况下甚至不合适(重构是合适的,正如其他人所建议的那样)。

然而,似乎很多java程序员都不知道CopyOnWriteArrayList(JDK自1.5以来的一部分),并且正在尝试将自己的解决方案推向同一个问题(迭代前的副本列表)。

答案 7 :(得分:1)

...从目录列表中删除非XML的文件...

List<File> files = Arrays.asList(dir.listFiles());

Iterator<File> i = files.iterator();

while (i.hasNext()) {
    File file = i.next();
    if (!file.getName().endsWith(".xml")) {
        i.remove();
    }
}

答案 8 :(得分:1)

与firebird84类似。但你可以使用removeAll(Collection c)api

for(String exitingPermission : existingPermissions){                
    //remove all permissions for the screen and add the new ones
    if(exitingPermission.split("_")[0].equals(screen)){
        removePermissions.add(exitingPermission);
    }
 }
existingPermissions.removeAll(removePermissions);