我们都知道,由于ConcurrentModificationException您无法执行以下操作:

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

即使没有多个线程。 无论如何。

解决此问题的最佳方法是什么? 如何在不引发此异常的情况下循环从集合中删除项目?

我还在这里使用任意Collection ,不一定是ArrayList ,所以您不能依赖get


#1楼

制作现有列表的副本,并遍历新副本。

for (String str : new ArrayList<String>(listOfStr))
{listOfStr.remove(/* object reference or index */);
}

#2楼

使用Eclipse Collections时 ,在removeIf定义的removeIf方法将起作用:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

使用Java 8 Lambda语法,可以这样编写:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

这里需要调用Predicates.cast() ,因为在Java 8的java.util.Collection接口上添加了默认的removeIf方法。

注意:我是Eclipse Collections的提交者。


#3楼

与Claudius的答案相同,带有for循环:

for (Iterator<Object> it = objects.iterator(); it.hasNext();) {Object object = it.next();if (test) {it.remove();}
}

#4楼

这可能不是最好的方法,但是对于大多数小情况,这应该可以接受:

“创建第二个空数组,并仅添加要保留的数组”

我不记得我从哪里读来的……为了公正起见,我将制作此Wiki,以希望有人找到它或只是不赚我不配的代表。


#5楼

我对以上问题有一个建议。 无需二级列表或任何额外的时间。 请找到一个示例,该示例将执行相同的操作,但使用不同的方式。

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {Object r = list.get(index);if( state ) {list.remove(index);index = 0;continue;}index += 1;
}

这样可以避免并发异常。


#6楼

这有效:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {if (iter.next() == 5) {iter.remove();}
}

我假设因为foreach循环是用于迭代的语法糖,所以使用迭代器无济于事...但是它为您提供了.remove()功能。


#7楼

Iterator.remove()是安全的,可以像这样使用它:

List<String> list = new ArrayList<>();// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {String string = iterator.next();if (string.isEmpty()) {// Remove the current element from the iterator and the list.iterator.remove();}
}

注意, Iterator.remove()是在迭代过程中修改集合的唯一安全方法。 如果在进行迭代时以任何其他方式修改了基础集合,则行为未指定。

来源: docs.oracle>收集接口


同样,如果您拥有ListIterator并想要添加项目,则可以使用ListIterator#add ,出于相同的原因,您可以使用Iterator#remove -它是允许的。


你的情况,你想从列表中删除,但同样的限制,如果试图putMap ,而迭代其内容。


#8楼

您可以像上面提到的那样直接使用迭代器,也可以保留第二个集合,然后将要删除的每个项目添加到新集合中,然后最后删除removeAll。 这使您可以继续使用for-each循环的类型安全性,但会增加内存使用量和cpu时间(除非您拥有非常大的列表或非常老的计算机,否则这不是一个大问题)

public static void main(String[] args)
{Collection<Integer> l = new ArrayList<Integer>();Collection<Integer> itemsToRemove = new ArrayList<Integer>();for (int i=0; i < 10; ++i) {l.add(new Integer(4));l.add(new Integer(5));l.add(new Integer(6));}for (Integer i : l){if (i.intValue() == 5)itemsToRemove.add(i);}l.removeAll(itemsToRemove);System.out.println(l);
}

#9楼

在Java 8中,可以使用新的removeIf方法 。 应用于您的示例:

Collection<Integer> coll = new ArrayList<>();
//populatecoll.removeIf(i -> i == 5);

#10楼

在这种情况下,通常的诀窍是(过去是)倒退:

for(int i = l.size() - 1; i >= 0; i --) {if (l.get(i) == 5) {l.remove(i);}
}

就是说,我很高兴您在Java 8中有了更好的方法,例如removeIfremoveIf filter


#11楼

由于问题已经得到解决,即最好的方法是使用迭代器对象的remove方法,因此我将详细介绍引发错误"java.util.ConcurrentModificationException"位置。

每个集合类都有一个私有类,该类实现Iterator接口并提供诸如next()remove()hasNext()

下一个代码看起来像这样...

public E next() {checkForComodification();try {E next = get(cursor);lastRet = cursor++;return next;} catch(IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}
}

在这里,方法checkForComodification被实现为

final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();
}

因此,如您所见,如果您明确尝试从集合中删除一个元素。 它导致modCount获得不同于expectedModCount ,导致异常ConcurrentModificationException


#12楼

如果ArrayList:remove(int index) System.arraycopy() index是最后一个元素的位置),则在没有System.arraycopy()情况下可以避免,并且不需要花费时间。

arraycopy时间增加if(index减少),通过list元素也减少!

最好的有效删除方法是-降序删除其元素: while(list.size()>0)list.remove(list.size()-1); //取O(1) while(list.size()>0)list.remove(0); //取O(factor(n))

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {Integer integer = rdm.nextInt();ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • 用于索引循环:1090毫秒
  • 对于desc索引: 519毫秒---最佳
  • 对于迭代器:1043毫秒

#13楼

ConcurrentHashMap或ConcurrentLinkedQueue或ConcurrentSkipListMap可能是另一个选择,因为即使您删除或添加项目,它们也不会抛出任何ConcurrentModificationException。


#14楼

for (Integer i : l)
{if (i.intValue() == 5){itemsToRemove.add(i);break;}
}

如果您跳过内部iterator.next()调用,则从列表中删除该元素之后便是捕获。 它仍然有效! 尽管我不建议编写这样的代码,但有助于理解其背后的概念:-)

干杯!


#15楼

与传统的for循环

ArrayList<String> myArray = new ArrayList<>();for (int i = 0; i < myArray.size(); ) {String text = myArray.get(i);if (someCondition(text))myArray.remove(i);else i++;}

#16楼

ListIterator允许您添加或删除列表中的项目。 假设您有一个Car对象列表:

List<Car> cars = ArrayList<>();
// add cars here...for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{if (<some-condition>){ carIterator().remove()}else if (<some-other-condition>){ carIterator().add(aNewCar);}
}

#17楼

人们断言不能从被foreach循环迭代的Collection中删除。 我只是想指出这在技术上是不正确的,并准确地描述了该假设背后的代码(我知道OP的问题非常先进,以至于不知道这一点)。

    for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikesif (obj.isTouched()) {untouchedSet.remove(obj);touchedSt.add(obj);break;  // this is key to avoiding returning to the foreach}}

不是您不能从迭代Colletion删除,而是一旦完成就无法继续迭代。 因此,上面的代码break了。

抱歉,如果此答案是有点专业的用例,并且更适合于我从此处到达的原始线程 ,则将该标记为该线程的副本(尽管此线程看上去更细微)并被锁定。


#18楼

最好的方法(推荐)是使用java.util.Concurrent包。 通过使用此包,您可以轻松避免此Exception。 参考修改后的代码

public static void main(String[] args) {Collection<Integer> l = new CopyOnWriteArrayList<Integer>();for (int i=0; i < 10; ++i) {l.add(new Integer(4));l.add(new Integer(5));l.add(new Integer(6));}for (Integer i : l) {if (i.intValue() == 5) {l.remove(i);}}System.out.println(l);}

#19楼

线程安全集合修改的示例:

public class Example {private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());public void removeFromQueue() {synchronized (queue) {Iterator<String> iterator = queue.iterator();String string = iterator.next();if (string.isEmpty()) {iterator.remove();}}}
}

#20楼

我知道这个问题对于Java 8来说已经太老了,但是对于那些使用Java 8的人来说,您可以轻松地使用removeIf():

Collection<Integer> l = new ArrayList<Integer>();for (int i=0; i < 10; ++i) {l.add(new Integer(4));l.add(new Integer(5));l.add(new Integer(6));
}l.removeIf(i -> i.intValue() == 5);

#21楼

我知道这个问题只是假设一个Collection ,而不是假设任何List 。 但是对于那些确实在使用List引用的阅读此问题的人,可以避免使用while -loop(在其中进行修改)时避免ConcurrentModificationException ,而如果您要避免使用Iterator (或者您通常希望避免使用Iterator ,或者避免使用它专门用于实现不同于在每个元素上从头到尾停止的循环顺序(我相信这是Iterator本身唯一可以执行的顺序)):

*更新:请参见下面的注释,以澄清类似情况也可以通过传统的 -for循环实现。

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){list.add(i);
}int i = 1;
while(i < list.size()){if(list.get(i) % 2 == 0){list.remove(i++);} else {i += 2;}
}

该代码没有ConcurrentModificationException。

在这里,我们看到循环不是从头开始,也不是在每个元素上停止(我相信Iterator本身不能做到)。

FWIW我们还看到getlist上被调用,如果它的引用只是Collection (而不是更具体的Collection List type),则无法完成List接口包括get ,但Collection接口没有。 如果不是因为这种差异,则list引用可以改为Collection (因此,从技术上讲,此答案将是直接答案,而不是切向答案)。

FWIWW相同的代码经过修改后可以在每个元素的起点开始停止运行(就像Iterator顺序一样):

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){list.add(i);
}int i = 0;
while(i < list.size()){if(list.get(i) % 2 == 0){list.remove(i);} else {++i;}
}

#22楼

另一种方法是创建arrayList的副本:

List<Object> l = ...List<Object> iterationList = ImmutableList.copyOf(l);for (Object i : iterationList) {if (condition(i)) {l.remove(i);}

}


#23楼

一种解决方案是旋转列表并删除第一个元素,以避免ConcurrentModificationException或IndexOutOfBoundsException

int n = list.size();
for(int j=0;j<n;j++){//you can also put a condition before removelist.remove(0);Collections.rotate(list, 1);
}
Collections.rotate(list, -1);

#24楼

尝试这一步(删除列表中等于i所有元素):

for (Object i : l) {if (condition(i)) {l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());}
}

遍历Collection,避免在循环中删除对象时避免ConcurrentModificationException相关推荐

  1. 在JavaScript中删除对象

    本文翻译自:Deleting Objects in JavaScript I'm a bit confused with JavaScript's delete operator. 我对JavaScr ...

  2. mysql for 循环删除_Java增强for循环中删除元素抛异常问题

    前言 最近突然想起刚毕业那会找工作时面试被问了个这样的问题.就是"使用增强for循环遍历ArrayList(List集合)时删除其中的元素是否会出现异常?".说实话当时真把我愣住了 ...

  3. es6删除对象的属性_javascript - 按对象属性从数组中删除对象

    javascript - 按对象属性从数组中删除对象 var listToDelete = ['abc', 'efg']; var arrayOfObjects = [{id:'abc',name:' ...

  4. Java循环中删除一个列表元素

    本文主要想讲述一下我对之前看到一篇文章的说法.假设跟你的想法有出入,欢迎留言.一起讨论. #3. 在循环中删除一个列表元素 考虑以下的代码.迭代过程中删除元素: ArrayList<String ...

  5. 如何在Java中处理ConcurrentModificationException? 在循环中从ArrayList中删除元素时要当心...

    从Java中从ArrayList中删除元素时常见的问题之一是ConcurrentModificationException. 如果您对索引使用经典的for循环或增强的for循环,并尝试使用remove ...

  6. java list循环中删除元素的坑

    背景 当我们要循环一个list中的元素,并且要删除某个元素的时候,一点要小心谨慎!其中深埋了好几个坑! 坑1 请看如下代码: /*** 测试删除集合中的空白元素*/ @Test public void ...

  7. php删除对象中的一个数组,PHP:从数组中删除对象

    unset函数可用于从PHP中的特定索引中删除数组对象- 示例$index = 2; $objectarray = array( 0 => array('label' => 'abc',  ...

  8. linux数组删除数据,JavaScript在数组的循环中删除元素

    在开发JavaScript应用的过程中,经常会遇到在循环中移除指定元素的需求. 按照常规的思路,就是对数组进行一个for循环,然后在循环里面进行if判断,在判断中删除掉指定元素即可. 但是实际情况往往 ...

  9. javascript在数组的循环中删除元素

    在开发JavaScript应用的过程中,经常会遇到在循环中移除指定元素的需求. 按照常规的思路,就是对数组进行一个for循环,然后在循环里面进行if判断,在判断中删除掉指定元素即可. 但是实际情况往往 ...

最新文章

  1. 逐飞mini车样品说明
  2. 基于tcp协议的socket
  3. CSS3圆圈动画放大缩小循环动画效果
  4. 人工神经网络发现生物神经网络,智源超高清电镜图像分割挑战赛开赛
  5. QT的QSslSocket类的使用
  6. multiprocessing python_Python教程:进程和线程amp;多进程
  7. 深入探讨一下如何打断点
  8. Codeforces 484E Sign on Fence(是持久的段树+二分法)
  9. php重写mysql类_如何成功重写旧的mysql-php代码与已弃用的mysql_ *函数?
  10. dell-xps-8930 台式机双硬盘 双系统安装 win10+Ubuntu
  11. 基于QT的推箱子小游戏设计
  12. SpringSecurity实战(六)-集成图形验证码-自定义认证实现
  13. 微信小程序生成海报并保存到本地(附带二维码生成)
  14. [UOJ198]时空旅行
  15. 数据结构例16.试设计一个算法, 使得在一个有序的单链表中插入一个元素后仍然有序。
  16. elasticsearch和elasticsearch-sql安装教程
  17. 科达视频系统设置服务器,科达KDV-VS视频录像点播系统用户手册.doc
  18. 【笔记】Polygon mesh processing 学习笔记(10)
  19. Java实现凑硬币或者最少硬币数
  20. C语言学习-Day4

热门文章

  1. C++中 =defaule 和 =delete什么意思
  2. Kotlin一大特色之空安全
  3. 第十二周项目一-实现复数类中的运算符重载(2)
  4. Flutter开发之Input-TextField-文本输入框(45)
  5. thinkphp概述2
  6. ##管家婆项目(service层)
  7. 关于网页显示乱码问题的一些个人见解(PHP、JSP...)
  8. jQuery插件之-selectList
  9. 汇编语言之寄存器使用(bx,si,di,bp)
  10. C#读写文本文件小结