在列表迭代期间从java.util.List中移除元素时,获取ConcurrentModificationExceptionexception?

@Test public void testListCur(){ List<String> li=new ArrayList<String>(); for(int i=0;i<10;i++){ li.add("str"+i); } for(String st:li){ if(st.equalsIgnoreCase("str3")) li.remove("str3"); } System.out.println(li); } 

当我运行这个代码时,我将抛出ConcurrentModificationException。

它看起来好像当我从列表中删除指定的元素,列表不知道它的大小已经改变。

我想知道这是一个常见的问题收集和删除元素?

我相信这是Iterator.remove()方法背后的目的,能够在迭代时从集合中移除元素。

例如:

 Iterator<String> iter = li.iterator(); while(iter.hasNext()){ if(iter.next().equalsIgnoreCase("str3")) iter.remove(); } 

请注意,这个exception并不总是表明一个对象已经被不同的线程同时修改。 如果单个线程发出一系列违反对象约定的方法调用,则该对象可能会抛出此exception。 例如,如果一个线程使用快速迭代器迭代集合的时候直接修改了一个集合,迭代器将会抛出这个exception

取自http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html

将Java 8的方式从无List的Iterator中移除是:

 li.removeIf(<predicate>) 

 List<String> li = new ArrayList<String>(); // ... li = li.removeIf(st -> !st.equalsIgnoreCase("str3")); 

是的,人们碰到它 – 问题是你不能修改列表,而迭代它。 过去我已经使用了两种select:

  1. 您可以跟踪要删除的项目的索引,然后在迭代完成后将其删除。
  2. 或者,您可以在迭代时将所有想要保留的列表复制到新列表中,然后在完成时丢弃旧列表。

这些选项假设您必须迭代列表才能find要删除的元素 – 在列表元素是可以testing的属性的复杂对象的情况下很有用。

在你的特定情况下,你甚至不需要迭代,因为你可以使用removeAll。 看看这里的API。 还有一些漂亮的方法,比如retainAll,抛弃所有不在参数中的东西。 只要列表中的对象实现了equals和hashcode,就可以使用remove / retain-like方法。 如果你不能依靠equals / hashcode来确定你的应用中的实例之间的平等,你将不得不自己去除….

我得到了这个问题,我认为更简单的方法与hvgotcodes给出的第二种方法是一样的。

或者,您可以在迭代时将所有想要保留的列表复制到新列表中,然后在完成时丢弃旧列表。

 @Test public void testListCur(){ List<String> li=new ArrayList<String>(); for(int i=0;i<10;i++){ li.add("str"+i); } List<String> finalLi = new ArrayList<String>(); for(String st:li){ if(st.equalsIgnoreCase("str3")){ // Do nothing } else { finalLi.add(st); } } System.out.println(finalLi); } 

ArrayList具有字段modCount – 收集修改的数量

当你调用方法时, iterator()会创build新的对象Itr 。 它具有现场expectedModCountexpectedModCount字段通过modCount值进行初始化。 当你调用

 li.remove("str3"); 

modCount增量。 你什么时候通过迭代器检查expectedModCount == modCount来尝试访问li

如果是false则抛出ConcurrentModificationException

因此,如果你得到的迭代器和修改后的集合 – 迭代器被认为是无效的,你不能使用它。

我觉得值得一提的是Java 8的版本

 @Test public void testListCur() { List<String> li = new ArrayList<String>(); for (int i = 0; i < 10; i++) { li.add("str" + i); } li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList()); System.out.println(li); } 

我以不同的方式循环…

 public void testListCur(){ List<String> li=new ArrayList<String>(); for(int i=0;i<10;i++){ li.add("str"+i); } for(int i=0; i<li.size(); i++) if(li.get(i).equalsIgnoreCase("str3")) li.remove(i--); System.out.println(li); } 

你可以创build一个你想从中删除元素的副本,直接在for-each循环中。 对我来说,这是最简单的方法。 像这样的东西:

 for (String stringIter : new ArrayList<String>(myList)) { myList.remove(itemToRemove); } 

希望能帮到你..

我认为最好的答案是来自bigdev.de,但我想添加一些东西(比如,如果项目从列表中删除,也许你想logging某处或某事):

 List<String> list = new ArrayList<>(); list.removeIf(a -> { boolean condition = a.equalsIgnoreCase("some condition"); if(condition) logger.info("Item removed from the list: " + a); return condition; }); 

试试这个(Java 8):

 list.removeIf(condition);