迭代时从 HashSet 中删除元素

发布于 2024-07-26 10:38:40 字数 770 浏览 2 评论 0原文

因此,如果我在迭代时尝试从 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);

这是一个解决方案,但我认为它不是很优雅:

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);

谢谢!

So, if I try to remove elements from a Java HashSet while iterating, I get a ConcurrentModificationException. What is the best way to remove a subset of the elements from a HashSet as in the following example?

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

ぺ禁宫浮华殁 2024-08-02 10:38:43

就像木材所说 - “Java 8 Collection 有一个很好的方法,称为removeIf,它使事情变得更容易和更安全”

这是解决您的问题的代码:

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

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

Like timber said - "Java 8 Collection has a nice method called removeIf that makes things easier and safer"

Here is the code that solve your problem:

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

Now your set contains only odd values.

别理我 2024-08-02 10:38:43

这是更现代的流方法:

myIntegerSet.stream().filter((it) -> it % 2 != 0).collect(Collectors.toSet())

但是,这会创建一个新集合,因此如果它是一个非常大的集合,那么内存限制可能会成为一个问题。

编辑:这个答案的先前版本建议使用 Apache CollectionUtils,但那是在 steam 出现之前。

Here's the more modern streams approach:

myIntegerSet.stream().filter((it) -> it % 2 != 0).collect(Collectors.toSet())

However, this makes a new set, so memory constraints might be an issue if it's a really huge set.

EDIT: previous version of this answer suggested Apache CollectionUtils but that was before steams came about.

九公里浅绿 2024-08-02 10:38:43

另一种可能的解决方案:

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

或者:

Integer[] copy = new Integer[set.size()];
set.toArray(copy);

for(Integer element : copy) {
    if(element % 2 == 0)
        set.remove(element);
}

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);
}
内心荒芜 2024-08-02 10:38:42

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

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

您经常会看到使用 for 循环而不是 while 循环的这种模式:

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

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

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 (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.

舂唻埖巳落 2024-08-02 10:38:42

您收到 ConcurrentModificationException 的原因是因为通过 Set.remove()(而不是 Iterator.remove())删除条目。 如果在迭代完成时通过 Set.remove() 删除条目,您将收到 ConcurrentModificationException。 另一方面,在这种情况下支持迭代时通过 Iterator.remove() 删除条目。

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

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

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

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.

The new for loop is nice, but unfortunately it does not work in this case, because you can't use the Iterator reference.

If you need to remove an entry while iteration, you need to use the long form that uses the Iterator directly.

for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
    Integer element = it.next();
    if (element % 2 == 0) {
        it.remove();
    }
}
九局 2024-08-02 10:38:42

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.

有趣的注释:

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-

Java 8 Collection has a nice method called removeIf that makes things easier and safer. From the API docs:

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().

From:
https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-

情仇皆在手 2024-08-02 10:38:42

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

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);

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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文