A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 张凯 中级黑马   /  2012-7-25 06:18  /  2143 人查看  /  9 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 张凯 于 2012-7-26 07:28 编辑

为什么
  1. public static void main(String[] args) {
  2. List<integer> list=new ArrayList<integer>(Arrays.<integer>asList(1,2,3,5));
  3. for(Integer l:list){
  4. if(l==3){
  5. list.remove(l);
  6. }
  7. }
  8. System.out.println(list);

  9. }
复制代码
不抛异常,而
  1. public static void main(String[] args) {
  2. List<integer> list=new ArrayList<integer>(Arrays.<integer>asList(1,2,3,5,6));
  3. for(Integer l:list){
  4. if(l==3){
  5. list.remove(l);
  6. }
  7. }
  8. System.out.println(list);

  9. }
复制代码
就抛Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:449)
at java.util.AbstractList$Itr.next(AbstractList.java:420)
at Test3.main(Test3.java:11)
???实在想不明白

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 赞一个!

查看全部评分

9 个回复

倒序浏览
  1. public static void main(String... args)
  2.     {
  3.                 List<Integer> list=new ArrayList<Integer>(Arrays.asList(1,2,3,5,6));
  4.                 for(Integer l:list){
  5.                         if(l==5)
  6.                         {
  7.                                list.remove(l);
  8.                         }
  9.                 }
  10.                 System.out.println(list);
  11.     }
复制代码
上面的代码就不抛异常,原因主要在于for循环,你在循环中进行删除操作,破坏了for循环导致异常。
回复 使用道具 举报
  1. public static void main(String... args)
  2.     {
  3.                 List<Integer> list=new ArrayList<Integer>(Arrays.asList(1,2,3,5,6));
  4.                 for(Integer l:list){
  5.                         if(l==5)
  6.                         {
  7.                                list.remove(l);
  8.                         }
  9.                 }
  10.                 System.out.println(list);
  11.     }
复制代码
上面的代码就不抛异常,原因主要在于for循环,你在循环中进行删除操作,破坏了for循环导致异常。
回复 使用道具 举报
/*
*
*总结:楼主,这个情况是并发访问的异常,以前我也没注意过
*                增强for循环会造成这个异常,现在想想,貌似增强for
*                循环在操作集合元素的时候,底层走的也是迭代器原理
*                造成并发访问异常的原因就是迭代器或增强for循环在操作
*                集合中元素的时候,list.remove()也在操作集合中的元素
* 原理:查看底层源代码,发现 abstractlist中包含一个modCount
                (modify count 修改次数)变量,初始值为0;集合每次调用
                add或者remove等方法时,都会使modCount加1或减1;即集合中的内容
                改变;而迭代器内部有一个expectedModCount预期修改次数的变量
                这个变量是记录集合的modCount变量的。
                如果这两个变量的值不等,就会出现ConcurrentModificationException
               
*
*
*/
import java.util.*;
class Test
{
        public static void main(String[] args)
        {
                //验证结果是:3后面如果只有一个元素,不会报异常,如果超过一个会出现异常
                List<Integer> list=new ArrayList<Integer>(Arrays.<Integer>asList(1,2,3,5));
                //楼主如果改为迭代器你能明白一些了吧?
                Iterator<Integer> it = list.iterator();
                while (it.hasNext())
                {
                        //迭代器在操作集合中元素的同时,list也在操作集合中的元素
                        //造成并发访问异常
                        Integer ig = it.next();
                        //我加上ig==5这个条件,它打印出的结果也是1,2,5 说明:5这个元素没有走迭代器
                                //是被强制打印出来的
                        if (ig==3 || ig==5)
                        {
                                list.remove(ig);
                        }
                }
                /*
                for(Integer l:list)
                {
                        if(l==3)
                        {
                                list.remove(l);
                        }
                }
                */
                System.out.println(list);
               
        }
}

评分

参与人数 1技术分 +1 收起 理由
田向向 + 1 赞一个!

查看全部评分

回复 使用道具 举报
补充:我也想知道怎么解决并发访问这个问题,加上同步其实可以解决,但是怎么加呢?加上同步会不会在数据量特别大的时候造成操作集合中的元素的效率会很慢?
回复 使用道具 举报
循环中不可以删除list中的元素。
可以这么解决:
public static void main(String[] args) {
List<integer> list=new ArrayList<integer>(Arrays.<integer>asList(1,2,3,5,6));
List removeList= new ArrayList();  //创建一个List专门存放被想删除元素,循环之后,用list.removeAll方法把元素删除
for(Integer l:list){
    if(l==3){
        removeList.add(l);    把想删除的元素先添加到removeList集合中
    }
}
list.removeAll(removeList);  //删除循环总想要删除的元素
    System.out.println(list);
}
还有一种简单的方法就是迭代器
public static void main(String[] args)
        {
           List<Integer> list=new ArrayList<Integer>(Arrays.<Integer>asList(1,2,3,5));
           Iterator<Integer> it = list.iterator();
                while (it.hasNext())
                {
                  if(l==3){
                               it.remove(l);
                           }
                }
回复 使用道具 举报
乐峰 中级黑马 2012-7-25 10:04:51
7#
在 List 取出元素的时候如果我们同时删除元素,就会发生异常
想要在迭代的过程中进行元素的增删改查动作,Iterator 就不行,可以使用其子接口ListIterator,而且该接口的对象只有通过List集合的listIterator方法获取,是List集合特有的取出元素方式。在迭代过程中,列表迭代器可以实现增删改查的动作。
public static void method2(List list2) {
list2.add("abcd11");
list2.add("abcd22");
list2.add("abcd33");
list2.add("abcd44");
ListIterator it = list2.listIterator();
while (it.hasNext()) {
Object obj = it.next();
if ("abcd22".equals(obj))  
it.add("hahaheihei");
else
System.out.println(it.next());
}
System.out.println(list2);
}
public static void method2(List list2) {
list2.add("abcd11");
list2.add("abcd22");
list2.add("abcd33");
list2.add("abcd44");
ListIterator it = list2.listIterator(); //迭代器在这里已经知道了集合中具体有多少个元素
while (it.hasNext()) {
System.out.println(it.next());
}
while(it.hasPrevious()){
System.out.println(it.previous());
}
}
回复 使用道具 举报
在ArrayList的父类AbstractList中定义了一个int型的属性:modCount,记录了ArrayList结构性变化的次数。
在ArrayList的所有涉及结构变化的方法中都增加modCount的值,包括:add()、remove()、addAll()、removeRange()及clear()方法。这些方法每调用一次,modCount的值就加1。
Iterator()也定义了一个int型的属性:expectedModCount,这个属性在Iterator()初始化时被赋予ArrayList对象的modCount属性的值:int expectedModCount = modCount;
在next()方法中调用了checkForComodification()方法,进行对修改的同步检查:

  1. final void checkForComodification() 
  2. { 
  3. if (modCount != expectedModCount)
  4.    throw new ConcurrentModificationException();
  5. }
复制代码
  1. public static void main(String[] args) {
  2. List<integer> list=new ArrayList<integer>(Arrays.<integer>asList(1,2,3,5)); //此处相当于往List中添加了四个元素。modCount的值为4.
  3. for(Integer l:list){ //底层为迭代器原理。初始化时expectedModCount=modCount为4.每次循环时调用next(),由其再调用checkForComodification()检测两个值是否相同。
  4. if(l==3){
  5. list.remove(l); //在第三次循环时,执行了一次remove(),modCount加一次,但是expectedModCount没有增加。但是没有执行下次循环前并没有调用checkForComodification()检测。所以没有报错。每次remove都会将后面的元素前移,长度减1.所以迭代结束,没有下次循环,所以没有报错。
  6. }
  7. }
  8. System.out.println(list);

  9. }
复制代码
根据以上推断,你的下一段代码会执行3后面的一次循环,所以会检测到expectedModCount!=modCount,报错。

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 赞一个!

查看全部评分

回复 使用道具 举报
张凯 中级黑马 2012-7-25 20:53:42
9#
吴立杰 发表于 2012-7-25 08:56
/*
*
*总结:楼主,这个情况是并发访问的异常,以前我也没注意过

解释的很详细,很感谢。
回复 使用道具 举报
张凯 发表于 2012-7-25 20:53
解释的很详细,很感谢。

共同努力!加油!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马