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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/626671/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:07:25  来源:igfitidea点击:

Java: How to remove elements from a list while iterating over/adding to it

javaalgorithmcollectionslist

提问by Arvodan

This question is a more special case of the problem described (and solved) in this question.

这个问题是在描述(和解决)问题的一个比较特殊的情况下这个问题

I have two methods, stopAndRemove(ServerObject server) and a close() method. The later should close all servers and remove them from the server list. The list is defined as

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

List<ServerObject> server. 

I do not want to have almost the same code from stopAndRemove in closeCurrentlyOpen, so I want to do something like:

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

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

This won't work, as this will cause a ConcurrentModificationException. I tried to make a copy of the list

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

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

and use that as the list for the foreach-loop. But then it might be possible that an other thread appends a Server to the servers list while I am iterating over copyList but closeCurrentlyOpen is supposed to result in an emtpy list. As the addServerToList method is synchronized to the servers-list, doing this

并将其用作 foreach 循环的列表。但是,当我迭代 copyList 但 closeCurrentlyOpen 应该导致一个空列表时,其他线程可能会将一个 Server 附加到服务器列表。由于 addServerToList 方法与服务器列表同步,因此执行此操作

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

will solve the problem with modifications. But then I can not synchronize the code in the stopAndRemove method which is necessary if it is directly called.

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

I seems to me that the design of this three methods probably needs a workover. Ideas anybody?

在我看来,这三种方法的设计可能需要修修补补。想法有人吗?

采纳答案by starblue

Split off a method stop() from stopAndRemove(). Then write the loop with an explicit iterator, do the stop and then iterator.remove().

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

"and" in a method name is a code smell.

方法名称中的“and”是一种代码气味。

回答by Eric Petroelje

When I've done this before, I always used the "old school" LinkedList collection, an Iterator, and the Iterator.remove() method to remove the current item.

当我以前这样做时,我总是使用“老派”LinkedList 集合、一个迭代器和 Iterator.remove() 方法来删​​除当前项目。

回答by Pesto

Refactor out all the ServerObject stopping code from stopAndRemove into a private stopServer method, and then do the removal separately in stopAndRemove and closeCurrentlyOpen. Then you can use a ListIterator to remove them (or just stop them all in a for loop and clear the list at the end).

将 stopAndRemove 中的所有 ServerObject 停止代码重构为私有 stopServer 方法,然后分别在 stopAndRemove 和 closeCurrentlyOpen 中进行删除。然后你可以使用 ListIterator 删除它们(或者只是在 for 循环中停止它们并在最后清除列表)。

回答by amit

You should get an iterator and remove using it. You are getting the exception because iterators are fail-fastin java.

你应该得到一个迭代器并使用它删除。您收到异常是因为迭代器在 Java 中是快速失败的

回答by Alex Miller

You might find this article about ConcurrentModificationExceptionhas some advice in this area.

您可能会发现这篇关于 ConcurrentModificationException 的文章在这方面有一些建议。

回答by Bob Gettys

Perhaps this is the wrong way to do it, but I always create a removal collection, which contains indexes or references to the objects that need to be removed. I then iterate over that collection and remove those indexes/objects from the original collection. Probably not the most efficient but it got the job done.

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

Instead of

代替

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

I use

我用

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

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

回答by Neeme Praks

Answering to the title of the question, not the specific details of the given example. In fact, this solution is not even appropriate in the given situation (refactoring is appropriate, as suggested by others).

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

However, it seems that many java programmers are not aware of CopyOnWriteArrayList(part of JDK since 1.5) and are trying to roll their own solutions to the same problem (copy list before iterating).

然而,似乎许多 Java 程序员不知道CopyOnWriteArrayList(自 1.5 以来的 JDK 的一部分),并试图推出自己的解决方案来解决同一问题(迭代前复制列表)。

回答by sparkyspider

... removing files that aren't XML from a directory list...

...从目录列表中删除不是 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();
    }
}

回答by Raskolnikov

Similar to firebird84. But u can use removeAll(Collection c) api

类似于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);