Java 循环遍历集合,避免在删除循环中的对象时出现ConcurrentModificationException

Java 循环遍历集合,避免在删除循环中的对象时出现ConcurrentModificationException,java,collections,iteration,Java,Collections,Iteration,我们都知道您无法执行以下操作,因为ConcurrentModificationException: for (Object i : l) { if (condition(i)) { l.remove(i); } } 但这显然有时有效,但并不总是有效。下面是一些特定的代码: publicstaticvoidmain(字符串[]args){ 集合l=新的ArrayList(); 对于(int i=0;i

我们都知道您无法执行以下操作,因为
ConcurrentModificationException

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}
但这显然有时有效,但并不总是有效。下面是一些特定的代码:

publicstaticvoidmain(字符串[]args){
集合l=新的ArrayList();
对于(int i=0;i<10;++i){
l、 增加(4);
l、 增加(5);
l、 增加(6);
}
对于(int i:l){
如果(i==5){
l、 删除(i);
}
}
系统输出打印LN(l);
}
当然,这会导致:

Exception in thread "main" java.util.ConcurrentModificationException
即使多个线程没有这样做。无论如何

这个问题的最佳解决方案是什么?如何在循环中从集合中移除项而不引发此异常

我在这里也使用了任意的
集合
,而不一定是
数组列表
,因此您不能依赖
获取
,这是有效的:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}
Iterator iter=l.Iterator();
while(iter.hasNext()){
if(iter.next()==5){
iter.remove();
}
}
我假设因为foreach循环是迭代的语法糖,所以使用迭代器不会有帮助。。。但是它提供了这个
.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();
    }
}
List List=new ArrayList();
//这是一种创建迭代器并调用iterator.hasNext()的聪明方法
//您可以在一个while循环中完成。这与执行以下操作相同:
//迭代器迭代器=list.Iterator();
//while(iterator.hasNext()){
for(Iterator Iterator=list.Iterator();Iterator.hasNext();){
String字符串=迭代器.next();
if(string.isEmpty()){
//从迭代器和列表中删除当前元素。
iterator.remove();
}
}
请注意,这是在迭代过程中修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改基础集合,则该行为未指定

资料来源:


同样,如果您有一个
列表迭代器
,并且想要添加项目,那么您可以使用,原因与您可以使用
迭代器#remove
的原因相同-它的设计允许它



在您的情况下,您试图从列表中删除,但如果在迭代内容时尝试将放入映射,则相同的限制适用。

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

publicstaticvoidmain(字符串[]args)
{
集合l=新的ArrayList();
Collection itemsToRemove=new ArrayList();
对于(int i=0;i<10;i++){
l、 加(4的整数);
l、 加(5的整数);
l、 加(6的整数);
}
for(整数i:l)
{
如果(i.intValue()==5){
itemsToRemove.add(i);
}
}
l、 removeAll(itemsToRemove);
系统输出打印LN(l);
}

既然这个问题已经得到了回答,也就是说,最好的方法是使用迭代器对象的remove方法,那么我将详细介绍抛出错误的位置
“java.util.ConcurrentModificationException”

每个集合类都有一个私有类,它实现迭代器接口,并提供诸如
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

复制现有列表并迭代新副本

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}
for(字符串str:newarraylist(listOfStr))
{
移除(/*对象引用或索引*/);
}
使用,上定义的方法
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);
MutableList=Lists.mutable.of(1,2,3,4,5);
list.removeIf(谓词.lessThan(3));
Assert.assertEquals(list.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);
MutableList=Lists.mutable.of(1,2,3,4,5);
removeIf(谓词.cast(整数->整数<3));
Assert.assertEquals(list.mutable.of(3,4,5),list);
这里需要调用
Predicates.cast()
,因为在java 8中的
java.util.Collection
接口上添加了默认的
removeIf
方法

注意:我是的提交者。

与for循环的答案相同:

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

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

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


我不记得我是从哪里读到这篇文章的……为了贾斯汀斯,我将制作这篇维基,希望有人能找到它,或者仅仅是为了不赢得我不配的声誉。

对于上述问题,我有一个建议。不需要第二次列表或任何额外的时间。请找一个例子,用不同的方式做同样的事情

//"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;
}
/“列表”是ArrayList
//“state”是一些布尔变量,当设置为true时,对象将从列表中删除
int指数=0;
while(索引
T
//"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;
}
Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);
for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}
//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
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}
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);
   }
}
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();
            }
        }
    }
}
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);
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;
    }
}
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;
    }
}
int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);
for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}
l.removeIf(current -> current == 5);
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, String> entry = iterator.next();
    if(entry.getKey().equals("test")) {
        iterator.remove();
    } 
}
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        //throws ConcurrentModificationException
        list.remove(it.next());
    }
}
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        it.remove()
    }
}