Java 迭代时从 HashSet 中删除元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1110404/
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
Remove Elements from a HashSet while Iterating
提问by
So, if I try to remove elements from a Java HashSetwhile iterating, I get a ConcurrentModificationException. What is the best way to remove a subset of the elements from a HashSetas in the following example?
因此,如果我在迭代时尝试从 Java HashSet 中删除元素,则会得到ConcurrentModificationException。如以下示例所示,从HashSet 中删除元素子集的最佳方法是什么?
Set<Integer> set = new HashSet<Integer>();
for(int i = 0; i < 10; i++)
set.add(i);
// Throws ConcurrentModificationException
for(Integer element : set)
if(element % 2 == 0)
set.remove(element);
Here is a solution, but I don't think it's very elegant:
这是一个解决方案,但我认为它不是很优雅:
Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>();
for(int i = 0; i < 10; i++)
set.add(i);
for(Integer element : set)
if(element % 2 == 0)
removeCandidates.add(element);
set.removeAll(removeCandidates);
Thanks!
谢谢!
采纳答案by Adam Paynter
You can manually iterate over the elements of the set:
您可以手动迭代集合的元素:
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
if (element % 2 == 0) {
iterator.remove();
}
}
You will often see this pattern using a for
loop rather than a while
loop:
您会经常使用for
循环而不是while
循环看到这种模式:
for (Iterator<Integer> i = set.iterator(); i.hasNext();) {
Integer element = i.next();
if (element % 2 == 0) {
i.remove();
}
}
As people have pointed out, using a for
loop is preferred because it keeps the iterator variable (i
in this case) confined to a smaller scope.
正如人们所指出的,使用for
循环是首选,因为它将迭代器变量(i
在这种情况下)限制在较小的范围内。
回答by dfa
you can also refactor your solution removing the first loop:
您还可以重构您的解决方案,删除第一个循环:
Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>(set);
for(Integer element : set)
if(element % 2 == 0)
removeCandidates.add(element);
set.removeAll(removeCandidates);
回答by dustmachine
Does it need to be whilst iterating? If all you're doing is filtering or selecting I would suggest using Apache Commons CollectionUtils. There are some powerful tools there and it makes your code "cooler."
迭代时需要吗?如果您所做的只是过滤或选择,我建议您使用 Apache Commons CollectionUtils。那里有一些强大的工具,它使您的代码“更酷”。
Here's an implementation that should provide what you need:
这是一个应该提供您需要的实现:
Set<Integer> myIntegerSet = new HashSet<Integer>();
// Integers loaded here
CollectionUtils.filter( myIntegerSet, new Predicate() {
public boolean evaluate(Object input) {
return (((Integer) input) % 2 == 0);
}});
If you find yourself using the same kind of predicate frequently you can pull that out into a static variable for reuse... name it something like EVEN_NUMBER_PREDICATE
. Some may see that code and declare it "hard to read" but it looks cleaner when you pull out the Predicate into a static. Then it's easy to see that we're doing a CollectionUtils.filter(...)
and that seems more readable (to me) than a bunch of loops all over creation.
如果您发现自己经常使用相同类型的谓词,则可以将其提取到静态变量中以供重用……将其命名为EVEN_NUMBER_PREDICATE
. 有些人可能会看到该代码并声明它“难以阅读”,但是当您将 Predicate 提取为静态时,它看起来更清晰。然后很容易看出我们正在做 aCollectionUtils.filter(...)
并且这似乎比整个创建过程中的一堆循环更具可读性(对我来说)。
回答by sjlee
The reason you get a ConcurrentModificationException
is because an entry is removed via Set.remove()as opposed to Iterator.remove(). If an entry is removed via Set.remove()while an iteration is being done, you will get a ConcurrentModificationException. On the other hand, removal of entries via Iterator.remove()while iteration is supported in this case.
你得到 a 的原因ConcurrentModificationException
是因为一个条目是通过Set.remove()而不是Iterator.remove()删除的。如果在迭代过程中通过Set.remove()删除条目,您将收到 ConcurrentModificationException。另一方面,在这种情况下支持在迭代时通过Iterator.remove()删除条目。
The new for loop is nice, but unfortunately it does not work in this case, because you can't use the Iterator reference.
新的 for 循环很好,但不幸的是它在这种情况下不起作用,因为您不能使用 Iterator 引用。
If you need to remove an entry while iteration, you need to use the long form that uses the Iterator directly.
如果需要在迭代时删除条目,则需要使用直接使用 Iterator 的长格式。
for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
Integer element = it.next();
if (element % 2 == 0) {
it.remove();
}
}
回答by alex2k8
An other possible solution:
另一种可能的解决方案:
for(Object it : set.toArray()) { /* Create a copy */
Integer element = (Integer)it;
if(element % 2 == 0)
set.remove(element);
}
Or:
或者:
Integer[] copy = new Integer[set.size()];
set.toArray(copy);
for(Integer element : copy) {
if(element % 2 == 0)
set.remove(element);
}
回答by risoldi
Java 8 Collection has a nice method called removeIf that makes things easier and safer. From the API docs:
Java 8 Collection 有一个叫做 removeIf 的好方法,它使事情变得更容易和更安全。来自 API 文档:
default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate.
Errors or runtime exceptions thrown during iteration or by the predicate
are relayed to the caller.
Interesting note:
有趣的注释:
The default implementation traverses all elements of the collection using its iterator().
Each matching element is removed using Iterator.remove().
来自:https: //docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-
回答by Getriax
Like timber said - "Java 8 Collection has a nice method called removeIf that makes things easier and safer"
就像木材说的 - “Java 8 Collection 有一个很好的方法叫做 removeIf,它使事情变得更容易和更安全”
Here is the code that solve your problem:
这是解决您的问题的代码:
set.removeIf((Integer element) -> {
return (element % 2 == 0);
});
Now your set contains only odd values.
现在你的集合只包含奇数值。