Java 迭代时从哈希集中删除元素

Java 迭代时从哈希集中删除元素,java,iteration,hashmap,hashset,Java,Iteration,Hashmap,Hashset,因此,如果我在迭代时尝试从Java哈希集中删除元素,就会得到ConcurrentModificationException。下面的示例中,从哈希集中删除元素子集的最佳方法是什么 Set<Integer> set = new HashSet<Integer>(); for(int i = 0; i < 10; i++) set.add(i); // Throws ConcurrentModificationException for(Integer ele

因此,如果我在迭代时尝试从Java哈希集中删除元素,就会得到ConcurrentModificationException。下面的示例中,从哈希集中删除元素子集的最佳方法是什么

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);
Set Set=newhashset();
对于(int i=0;i<10;i++)
增加(i);
//抛出ConcurrentModificationException
for(整型元素:集合)
if(元素%2==0)
设置。移除(元素);
这里有一个解决方案,但我认为它不是很优雅:

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);
Set Set=newhashset();
Collection removeCandidates=新建LinkedList();
对于(int i=0;i<10;i++)
增加(i);
for(整型元素:集合)
if(元素%2==0)
removeCandidates.add(元素);
set.removeAll(removeCandidates);

谢谢

您可以手动迭代集合中的元素:

Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    if (element % 2 == 0) {
        iterator.remove();
    }
}

正如人们指出的,最好使用
for
循环,因为它将迭代器变量(
i
在本例中)限制在较小的范围内。

您还可以重构解决方案,删除第一个循环:

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);
Set Set=newhashset();
Collection removeCandidates=新链接列表(集合);
for(整型元素:集合)
if(元素%2==0)
removeCandidates.add(元素);
set.removeAll(removeCandidates);

以下是更现代的streams方法:

myIntegerSet.stream().filter((it) -> it % 2 != 0).collect(Collectors.toSet())
然而,这会产生一个新的集合,所以如果它是一个非常大的集合,内存约束可能是一个问题


编辑:此答案的早期版本建议使用Apache CollectionUtils,但那是在steams出现之前。

您获得
ConcurrentModificationException的原因是通过Set.remove()而不是迭代器.remove()删除条目。如果在迭代过程中通过Set.remove()删除一个条目,您将得到一个ConcurrentModificationException。另一方面,通过Iterator.remove()删除条目,在这种情况下支持迭代

新的for循环很好,但不幸的是,它在这种情况下不起作用,因为您不能使用迭代器引用

如果需要在迭代时删除条目,则需要使用直接使用迭代器的长格式

for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
    Integer element = it.next();
    if (element % 2 == 0) {
        it.remove();
    }
}
for(Iterator it=set.Iterator();it.hasNext();){
整数元素=it.next();
if(元素%2==0){
it.remove();
}
}

另一种可能的解决方案:

for(Object it : set.toArray()) { /* Create a copy */
    Integer element = (Integer)it;
    if(element % 2 == 0)
        set.remove(element);
}
或:


Java8集合有一个名为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.
发件人:

正如timber所说,“Java8集合有一个很好的方法,叫做removeIf,它使事情变得更简单、更安全”

以下是解决问题的代码:

set.removeIf((Integer element) -> {
    return (element % 2 == 0);
});


现在,您的集合只包含奇数值。

我更喜欢
for
而不是
while
,但每个值都是自己的。我自己也使用
。我使用
while
希望能使示例更清晰。我更喜欢
for
主要是因为迭代器变量被限制在循环的范围内。如果使用
while
则迭代器的范围比需要的范围大。我更喜欢while,因为它看起来更清晰。如果要分解代码,迭代器的作用域不应该成为问题。有关分解代码的更多信息,请参阅贝克的书《测试驱动开发》或福勒的《重构》。我不建议这样做,因为它引入了隐藏的时间耦合。@RomainF。-你所说的隐藏的时间耦合是什么意思?你是说线程安全吗?第二,我也不建议这样做,但这个解决方案确实有其优点。是的,for循环会产生副作用,但我同意它可能是最可读的解决方案,除非您使用的是Java8。否则,只需使用“removeIf”方法。我认为这个答案忽略了第一个循环只有一个
HashSet
,可以从中删除某些元素如果您在循环过程中不仅删除了现有元素,而且还向集合中添加了新元素,那么这是最好的解决方案。。。现在有一种Java-8方法可以做到这一点,它可以说更干净。有没有更好的方法可以使用,或者您只是指使用lambda代替匿名内部类的功能?下面是更现代的方法:
myIntegerSet.stream().filter((it)->it%2!=0)。collect(Collectors.toSet())
示例:
integerSet.removief(integer->integer.equals(5));
@您的代码不应该实际调用它。next()?谢谢。修复了。“元素”是在什么时候实例化的?呃。修复了。谢谢。
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.
The default implementation traverses all elements of the collection using its iterator(). 
Each matching element is removed using Iterator.remove().
set.removeIf((Integer element) -> {
    return (element % 2 == 0);
});