首页 > 试题广场 >

下面程序的运行结果() &nbs...

[单选题]
下面程序的运行结果()
        List<String> aa = new ArrayList<String>();
        aa.add("F1");
        aa.add("F2");
        aa.add("F3");
        for (String temp : aa) {
            if ("F3".equals(temp)) {
                aa.remove(temp);
            }
        }
        
        for (String temp : aa){
             System.out.println(temp);
        }

  • 抛异常
  • F1F2
  • F1F2F3
  • F1
就这么说吧,对于集合的三种遍历方式删除:
1.普通for循环:可以删除
        注意每次删除之后索引要--
2.Iterator遍历:可以删除
        不过要使用Iterator类中的remove方法,如果用List中的remove方***报错
3.增强for循环foreach:不能删除
        强制用List中的remove方***报错
发表于 2019-03-17 22:29:38 回复(5)
Java集合中有一种被称为fail-fast的错误机制,当多线程对集合进行操作时(一个线程使用迭代器遍历集合;另一个线程修改集合内容)会抛出ConcurrentModificationException异常
发表于 2019-03-14 20:15:11 回复(2)
 ———————————————— 
版权声明:本文为CSDN博主「enable1234___」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Enable1234___/article/details/53792440


foreach遍历集合,相当于:
for(I #i = expression.iterator; #i.hasNext(); ){
    var x = #i.next();
}
第一步:进入hasNext()方法。

public boolean hasNext() {
            return cursor != size;
        }
 
        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();//此处报错
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
    final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
}

第二步,进入checkForComodification()方法
final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

}

第三步,判断是否相等,抛出异常。
ArrayList是在于foreach方式遍历元素的时候,是生成iterator,然后使用iterator遍历。在生成iterator的时候,会保存一个expectedModCount参数,这个是生成iterator的时候List中修改元素的次数。如果你在遍历过程中删除元素,List中modCount就会变化,如果这个modCount和exceptedModCount不一致,就会抛出异常,这个是为了安全的考虑。看看list的remove源码:

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
}
看,并没有对expectedModCount进行任何修改,导致expectedModCount和modCount不一致,抛出异常。所以,遍历list删除元素一律用Iterator这样不会报错,看看Iterator的remove()方法的源码,是对expectedModCount重新做了赋值处理的,如下:
public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
 
 
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;//处理expectedModCount
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }


这样的话保持expectedModCount = modCount相等,就不会报出错了。
2)是不是foreach所有的list删除操作都会报出这个错呢

其实不一定,有没有发现如果删除的元素是倒数第二个数的话,其实是不会报错的,为什么呢,来一起看看。
之前说了foreach循环会走两个方法hasNext() 和next()。如果不想报错的话,只要不进next()方法就好啦,看看hasNext()的方法。

public boolean hasNext() {
       return cursor != size;
}
那么就要求hasNext()的方法返回false了,即cursor == size。其中cursor是Itr类(Iterator子类)中的一个字段,用来保存当前iterator的位置信息,从0开始。cursor本身就是游标的意思,在数据库的操作中用的比较多。只要curosr不等于size就认为存在元素。由于Itr是ArrayList的内部类,因此直接调用了ArrayList的size字段,所以这个字段的值是动态变化的,既然是动态变化的可能就会有问题出现了。
我们以上面的代码为例,当到倒数第二个数据也就是”4”的时候,cursor是4,然后调用删除操作,此时size由5变成了4,当再调用hasNext判断的时候,cursor==size,就会调用后面的操作直接退出循环了。我们可以在上面的代码添加一行代码查看效果:

 for (String item : list) {
            System.out.println(item);
            if (item.equals("4")) {
                list.remove(item);
            }
 }
输出是:1 2 3 4 
这样的话就可以看到执行到hasNext()方法就退出了,也就不会走后面的异常了。
由此可以得出,用foreach删除list元素的时候只有倒数第二个元素删除不会报错,其他都会报错,所以用Iterator啦。


发表于 2019-08-20 09:34:29 回复(0)
出现异常:ConcurrentModificationException.
用foreach的方式会报错,因为foreach底层是用迭代器实现的,迭代器在进行迭代的时候不允许集合对数据进行增删操作(检测到并发修改)。一般使用迭代器遍历,迭代器操作(add.remove,set)或者使用普通for循环遍历,使用list.remove/add操作。
编辑于 2019-08-08 19:48:22 回复(0)
在迭代元素时,不建议使用ArrayList.remove()。 可能会导致ConcurrentModificationException;
如果想在迭代时删除元素,建议使用Iterator.remove()方法

发表于 2019-07-17 11:59:55 回复(0)
            if("F2".equals(temp)) {
                aa.remove(temp);
            }
要是改成F2,程序不会报错
发表于 2019-07-16 15:18:48 回复(2)
迭代器内部维护着一些索引位置的相关数据,迭代过程中不能发生结构性变化(插入或者删除)。迭代过程中不能使用容器的删除方法,可以使用迭代器的remove()方法进行删除。
发表于 2019-03-17 00:27:48 回复(0)
在用Iterator遍历ArrayList的时候,如果调用ArrayList的remove方法使得ArrayList的大小发生了改变,那么会抛出ConcurrentModificationException异常。
在这里for (String temp : aa)遍历操作就是通过Iterator实现的。
编辑于 2019-10-21 17:08:33 回复(3)
报错原因:如图

底层迭代器

arraylist内部实现的迭代器

出现异常的原因

进去看

迭代器初始化的时候默认
int expectedModCount = modCount;
你在迭代的时候调用了arraylist的remove()导致modCount++而expectedModcount还是初始化时候的值。
导致不相等走这个逻辑抛出 throw new ConcurrentModificationException();
如果改成remove("F2")不会报错因为这个方法返回false不走next()


如果使用迭代器的remove()方法可以看出有个modCount =expectedModCount复制过程

其实抛不抛异常只要看他什么情况下走throw逻辑就行

发表于 2019-08-13 16:08:06 回复(3)
在用Iterator遍历ArrayList的时候,如果调用ArrayList的remove方法使得ArrayList的大小发生了改变,那么会抛出ConcurrentModificationException异常。 在这里for (String temp : aa)遍历操作就是通过Iterator实现的。
发表于 2019-09-27 08:39:50 回复(0)
fail-fast机制,当迭代集合的过程中,集合结构发生改变(add或者remove),就有可能抛出ConcurrentModificationException
发表于 2019-09-10 19:28:41 回复(0)
增强for循环foreach:不能删除,强制用List中的remove方***报错,就是不能在list循环中删除值,不然会改变list的大小,一般是再循环中获取索引,在循环外删除
编辑于 2019-08-15 11:13:18 回复(0)
这道题涉及到一个bug怎么报错呀?
发表于 2019-07-15 07:31:58 回复(0)