如何避免“ConcurrentModificationException”同时从ArrayList中删除元素,而迭代呢?
我试图从ArrayList
删除一些元素,而像这样迭代它:
for (String str : myArrayList) { if (someCondition) { myArrayList.remove(str); } }
当然,当迭代myArrayList
ConcurrentModificationException
尝试从列表中删除项目时,我得到了ConcurrentModificationException
。 有没有一些简单的解决scheme来做到这一点?
使用Iterator
并调用remove()
:
Iterator<String> iter = myArrayList.iterator(); while (iter.hasNext()) { String str = iter.next(); if (someCondition) iter.remove(); }
作为别人答案的替代,我总是这样做:
List<String> toRemove = new ArrayList<>(); for (String str : myArrayList) { if (someCondition) { toRemove.add(str); } } myArrayList.removeAll(toRemove);
这将避免你不得不直接处理迭代器,但需要另一个列表。 无论出于何种原因,我总是喜欢这条路线。
Java 8用户可以这样做: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c")); list.removeIf(e -> (someCondition));
它将删除列表中的某些条件满足的元素
你必须使用迭代器的remove()方法,这意味着没有增强的for循环:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) { iterator.next(); if (someCondition) { iterator.remove(); } }
不不不!
在单一的threated任务,你不需要使用Iterator,此外,CopyOnWriteArrayList(由于性能命中)。
解决scheme要简单得多: 尝试使用规范for循环而不是for-each循环 。
根据Java版权所有人(几年前的Sun,现在是Oracle) 的每一个循环指南 ,它使用迭代器遍历集合,并隐藏它,使代码看起来更好。 但不幸的是,正如我们所看到的,它产生了比利润更多的问题,否则这个话题就不会出现。
例如,这个代码在修改的ArrayList上进入下一个迭代时将导致java.util.ConcurrentModificationException:
// process collection for (SomeClass currElement: testList) { SomeClass founDuplicate = findDuplicates(currElement); if (founDuplicate != null) { uniqueTestList.add(founDuplicate); testList.remove(testList.indexOf(currElement)); } }
但下面的代码工作得很好:
// process collection for (int i = 0; i < testList.size(); i++) { SomeClass currElement = testList.get(i); SomeClass founDuplicate = findDuplicates(currElement); if (founDuplicate != null) { uniqueTestList.add(founDuplicate); testList.remove(testList.indexOf(currElement)); i--; //to avoid skipping of shifted element } }
所以,尝试使用索引方法迭代集合,避免for-each循环,因为它们不是等价的! For-each循环使用一些内部迭代器,它们检查集合修改并抛出ConcurrentModificationExceptionexception。 为了确认这一点,当使用我发布的第一个示例时,仔细查看打印的堆栈跟踪:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at TestFail.main(TestFail.java:43)
对于multithreading使用相应的多任务处理(如同步关键字)。
虽然其他build议的解决scheme工作,如果你真的想让解决scheme成为线程安全的,你应该用CopyOnWriteArrayListreplaceArrayList
//List<String> s = new ArrayList<>(); //Will throw exception List<String> s = new CopyOnWriteArrayList<>(); s.add("B"); Iterator<String> it = s.iterator(); s.add("A"); //Below removes only "B" from List while (it.hasNext()) { s.remove(it.next()); } System.out.println(s);
如果你想在遍历期间修改你的List,那么你需要使用Iterator
。 然后你可以使用iterator.remove()
在遍历期间删除元素。
List myArrayList = Collections.synchronizedList(new ArrayList()); //add your elements myArrayList.add(); myArrayList.add(); myArrayList.add(); synchronized(myArrayList) { Iterator i = myArrayList.iterator(); while (i.hasNext()){ Object object = i.next(); } }
一种替代方法是将List
转换为array
,迭代它们并根据您的逻辑从List
直接删除它们。
List<String> myList = new ArrayList<String>(); // You can use either list or set myList.add("abc"); myList.add("abcd"); myList.add("abcde"); myList.add("abcdef"); myList.add("abcdefg"); Object[] obj = myList.toArray(); for(Object o:obj) { if(condition) myList.remove(o.toString()); }
您可以使用迭代器remove()函数从底层集合对象中删除该对象。 但在这种情况下,您可以从列表中删除相同的对象而不是其他任何对象。
从这里