通过集合进行迭代,避免在循环中删除对象时进行contrentModification Exception
我们都知道,由于 conturrentModificationException
:
for (Object i : l) {
if (condition(i)) {
l.remove(i);
}
}
但这显然有时可以,但并非总是如此。这是一些特定的代码:
public static void main(String[] args) {
Collection<Integer> l = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
l.add(4);
l.add(5);
l.add(6);
}
for (int i : l) {
if (i == 5) {
l.remove(i);
}
}
System.out.println(l);
}
当然,这会导致:
Exception in thread "main" java.util.ConcurrentModificationException
即使多个线程没有这样做。反正。
解决这个问题的最佳解决方案是什么?如何在不抛出此例外的情况下以循环中的循环中删除一个项目?
我还在这里使用任意收集
,不一定是 arrayList
,因此您不能依靠 get
。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
iterator.remove()< /code>
是安全的,您可以这样使用它:
请注意
iterator.remove()
是在迭代过程中修改收集的唯一安全方法;如果在进行迭代进行时,以任何其他方式修改了基础集合,则该行为是未指定的。资料来源: docs.oracle&gt;集合接口
类似地,如果您有
ListIterator
并想要 add 项目,则可以使用listiterator#add
,出于相同的原因,您可以使用Iterator#remove
&nbsp; - 它旨在允许它。在您的情况下,您尝试从列表中删除,但是如果尝试
将
put 添加到地图
时,则适用相同的限制。Iterator.remove()
is safe, you can use it like this:Note that
Iterator.remove()
is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.Source: docs.oracle > The Collection Interface
And similarly, if you have a
ListIterator
and want to add items, you can useListIterator#add
, for the same reason you can useIterator#remove
— it's designed to allow it.In your case you tried to remove from a list, but the same restriction applies if trying to
put
into aMap
while iterating its content.这起作用了:
我假设,由于foreach循环是用于迭代的句法糖,因此使用迭代器无济于事...但是它为您提供了此
.remove()
函数。This works:
I assumed that since a foreach loop is syntactic sugar for iterating, using an iterator wouldn't help... but it gives you this
.remove()
functionality.使用Java 8,您可以使用新的
remove
方法。应用于您的示例:一个简单的测试作为示例:
当您比较两个列表并希望从两者中删除时,它甚至可以工作。
但是,如果其中一个具有重复项,请确保它在内部循环中迭代,因为对于内部列表,它将删除所有符合条件的 elements 元素,但对于外部列表,当删除任何元素时,它将立即返回并停止检查。
该测试将失败:
With Java 8 you can use the new
removeIf
method. Applied to your example:A simple test as example:
It even works when you compare two lists and want to remove from both.
However, if one of the list has duplicates, make sure it's iterated in the inner loop, because for inner list, it will remove all elements meeting the criteria, but for outer list, when any element is removed, it will return immediately and stops checking.
This test will fail:
由于问题已经回答,即最好的方法是使用迭代对象的删除方法,因此我将进入错误
“ java.util.concurrentmodification exception”
的详细信息。 。每个集合类都有一个私人类,该类实现了迭代器接口,并提供了
next()
,remove()
and hasnext() hasnext()之类的方法。接下来的代码看起来像这样...
这里的方法
CheckForcomodification
是这样的,如您所见,如果您明确尝试从集合中删除元素,则可以看到。它导致
modCount
与greengemodcount
不同,导致异常concurrentModificationException
。Since the question has been already answered i.e. the best way is to use the remove method of the iterator object, I would go into the specifics of the place where the error
"java.util.ConcurrentModificationException"
is thrown.Every collection class has a private class which implements the Iterator interface and provides methods like
next()
,remove()
andhasNext()
.The code for next looks something like this...
Here the method
checkForComodification
is implemented asSo, as you can see, if you explicitly try to remove an element from the collection. It results in
modCount
getting different fromexpectedModCount
, resulting in the exceptionConcurrentModificationException
.您可以像提到的那样直接使用迭代器,也可以保留第二个集合,然后将要删除的每个项目添加到新集合中,然后在末尾删除。这使您可以继续以增加内存使用时间和CPU时间为代价来使用for-EAK循环的类型安全(除非您确实有真正,很大的列表或真正旧的计算机,否则不应该是一个巨大的问题)
You can either use the iterator directly like you mentioned, or else keep a second collection and add each item you want to remove to the new collection, then removeAll at the end. This allows you to keep using the type-safety of the for-each loop at the cost of increased memory use and cpu time (shouldn't be a huge problem unless you have really, really big lists or a really old computer)
情况下
在这种 >在流上。
In such cases a common trick is (was?) to go backwards:
That said, I'm more than happy that you have better ways in Java 8, e.g.
removeIf
orfilter
on streams.与 claudius
Same answer as Claudius with a for loop:
制作现有列表的副本,并在新副本上进行迭代。
Make a copy of existing list and iterate over new copy.
使用 eclipse collections ,方法
emove> remove> remove
在 mutableCollection 将有效: Java 8 lambda语法可以写如下:
在此需要呼叫
predicates.cast.cast.cast()
,因为在Java上添加了默认的
Java 8中的接口。remove> remove
方法。 util.Collection注意:我是 eclipse collections 。
With Eclipse Collections, the method
removeIf
defined on MutableCollection will work:With Java 8 Lambda syntax this can be written as follows:
The call to
Predicates.cast()
is necessary here because a defaultremoveIf
method was added on thejava.util.Collection
interface in Java 8.Note: I am a committer for Eclipse Collections.
人们断言一个不能从foreach循环迭代的集合中删除。我只是想指出,这是在技术上是不正确的,并确切地描述了(我知道OP的问题是如此先进,以至于可以避免知道这一点)该假设背后的代码:
不是你不能从迭代
colletion
中删除,而是一旦执行,就无法继续迭代。因此,上面的代码中的中断
。抱歉,如果这个答案是一个专业的用例,并且更适合原始的我从这里到达这里,这被标记为对此的重复(尽管看起来更细微)并被锁定。
People are asserting one can't remove from a Collection being iterated by a foreach loop. I just wanted to point out that is technically incorrect and describe exactly (I know the OP's question is so advanced as to obviate knowing this) the code behind that assumption:
It isn't that you can't remove from the iterated
Colletion
rather that you can't then continue iteration once you do. Hence thebreak
in the code above.Apologies if this answer is a somewhat specialist use-case and more suited to the original thread I arrived here from, that one is marked as a duplicate (despite this thread appearing more nuanced) of this and locked.
带有传统的循环
With a traditional for loop
concurrenthashmap href =“ https://docs.oracle.com/javase/7/docs/api/java/java/util/concurrent/concurrent/concurrentlinkedqueue.html” rel =“ noreferrer”> concurrentlinkedqueue href =“ https://docs.oracle.com/javase/7/docs/api/java/java/java/util/concurrent/concurrentskiplistmap.html” rel =“ noreferrer”> concurrentskiplistmap 可能是另一个选项即使您删除或添加项目,也切勿抛出任何contrentModification Exception。
ConcurrentHashMap or ConcurrentLinkedQueue or ConcurrentSkipListMap may be another option, because they will never throw any ConcurrentModificationException, even if you remove or add item.
另一种方法是将您的阵列列表的副本仅用于迭代:
Another way is to use a copy of your arrayList just for iteration:
Listiterator
允许您在列表中添加或删除项目。假设您有car
对象的列表:A
ListIterator
allows you to add or remove items in the list. Suppose you have a list ofCar
objects:现在,您可以使用以下代码删除
Now, You can remove with the following code
我知道这个问题太老了,无法与Java 8有关,但是对于使用Java 8的人,您可以轻松地使用Removeif():
I know this question is too old to be about Java 8, but for those using Java 8 you can easily use removeIf():
Java并发修改异常
解决方案:迭代器
remove()
MethodJava Concurrent Modification Exception
Solution: iterator
remove()
methodsynchronize
[About]我对上述问题有一个建议。无需辅助列表或任何额外的时间。请找到一个可以做同样的事情但以不同方式进行相同工作的示例。
这将避免并发例外。
I have a suggestion for the problem above. No need of secondary list or any extra time. Please find an example which would do the same stuff but in a different way.
This would avoid the Concurrency Exception.
如果您跳过内部迭代仪()调用,则捕获量是在后面将元素从列表中删除。它仍然有效!尽管我不建议这样写这样的代码,但它有助于理解其背后的概念:-)
欢呼!
The catch is the after removing the element from the list if you skip the internal iterator.next() call. it still works! Though I dont propose to write code like this it helps to understand the concept behind it :-)
Cheers!
线程安全收集修改的示例:
Example of thread safe collection modification:
我知道这个问题仅假定一个
集合
,而不是更具体地说明任何list
。但是对于那些确实正在使用list
参考的人来说在其中进行修改),而不是要避免迭代器
(如果您想避免使用它,或者特别避免使用它以达到与启动到结束停止不同的循环顺序每个元素[我认为这是唯一的订单迭代器
本身可以做到]):*更新:请参阅下面的注释,可以使用传统 - 也可以实现类似的内容。 for-loop。
no conturrentModification exception从该代码中。
我们看到循环不是从开始的开始,而不是在 中停止
迭代器
本身无法做到)。fwiw我们还可以在
list
上看到get
,如果其引用仅为collection
(而不是更具体的list -type的
收集
) -list
接口包括get
get ,但Collection
接口>接口不知道。如果不是为了这种差异,那么list
参考可以是collection
[因此,从技术上讲,此答案将是直接答案,而不是切向答案]。FWIWW相同的代码在修改后仍然可以在每个元素的stop stop中开始工作(就像
Iterator
订单一样):I know this question assumes just a
Collection
, and not more specifically anyList
. But for those reading this question who are indeed working with aList
reference, you can avoidConcurrentModificationException
with awhile
-loop (while modifying within it) instead if you want to avoidIterator
(either if you want to avoid it in general, or avoid it specifically to achieve a looping order different from start-to-end stopping at each element [which I believe is the only orderIterator
itself can do]):*Update: See comments below that clarify the analogous is also achievable with the traditional-for-loop.
No ConcurrentModificationException from that code.
There we see looping not start at the beginning, and not stop at every element (which I believe
Iterator
itself can't do).FWIW we also see
get
being called onlist
, which could not be done if its reference was justCollection
(instead of the more specificList
-type ofCollection
) -List
interface includesget
, butCollection
interface does not. If not for that difference, then thelist
reference could instead be aCollection
[and therefore technically this Answer would then be a direct Answer, instead of a tangential Answer].FWIWW same code still works after modified to start at beginning at stop at every element (just like
Iterator
order):一种解决方案可能是旋转列表并删除第一个元素,以避免conturrentModificationException或indexoutofBoundSexception
One solution could be to rotate the list and remove the first element to avoid the ConcurrentModificationException or IndexOutOfBoundsException
尝试这个(删除等于
i
的列表中的所有元素):Try this one (removes all elements in the list that equal
i
):您可以使用一个时循环。
You can use a while loop.
我最终使用了此
consurrentModificationException
,同时使用stream()。map()
方法迭代列表。但是,(:)在迭代和修改列表时没有引发异常。这是代码段,如果它对任何人提供帮助:
在这里,我在
arrayList&lt; buildentity&gt;
上迭代,并使用list.remove(obj)对其进行修改I ended up with this
ConcurrentModificationException
, while iterating the list usingstream().map()
method. However thefor(:)
did not throw the exception while iterating and modifying the the list.Here is code snippet , if its of help to anyone:
here I'm iterating on a
ArrayList<BuildEntity>
, and modifying it using the list.remove(obj)如果使用hashmap,则在Java(8+)的较新版本中,您可以选择3个选项中的每个选项:
If using HashMap, in newer versions of Java (8+) you can select each of 3 options:
最好的方法(推荐)是使用
java.util.concurrent
软件包。经过使用此软件包,您可以轻松避免此例外。参考
修改的代码:
The best way (recommended) is use of
java.util.concurrent
package. Byusing this package you can easily avoid this exception. Refer
Modified Code:
迭代器 在另一个线程也修改该集合时并不总是有帮助的。我曾尝试过多种方法,但随后意识到手动穿越该系列要安全得多(向后移动):
Iterators are not always helpful when another thread also modifies the collection. I had tried many ways but then realized traversing the collection manually is much safer (backward for removal):
迭代器在某些情况下也可能引起 contrentModificationException 。
如果单线线程一次访问列表,这将起作用。
您可以使用
copyOnwritearRaylist
如果您的列表从 mutliple threads访问。但是请记住,如果您的列表经常更改,它将导致性能问题。copyOnwritearRaylist
如果您的列表不经常更新,则表现更好。Iterator is not foolproof and can also cause ConcurrentModificationException in some cases.
This will work if single thread is accessing list at a time.
You can use
CopyOnWriteArrayList
if your list is access from mutliple threads. But remember it will cause performance issues if your list is changing frequently.CopyOnWriteArrayList
will perform better if your list is not updating frequently.如果 arrayList:remove(int index) - 如果(索引是最后一个元素的位置),则避免没有
system.ArrayCopy()
,并且没有时间为此。如果(索引减少),列表元素的元素也会减少,则阵列时间会增加!
最好的有效删除方法是按降序删除其元素:
while(list.size()&gt; 0)list.remove(list.size() - 1);
// take o(1)while(list.size()&gt; 0)list.Remove(0);
// take o(fortorial(n))In case ArrayList:remove(int index)- if(index is last element's position) it avoids without
System.arraycopy()
and takes not time for this.arraycopy time increases if(index decreases), by the way elements of list also decreases!
the best effective remove way is- removing its elements in descending order:
while(list.size()>0)list.remove(list.size()-1);
//takes O(1)while(list.size()>0)list.remove(0);
//takes O(factorial(n))