Java使用for和迭代器Iterator中remove比较

本文探讨了在Java中使用for循环和Iterator删除集合元素的区别。通过实例解析,指出在for循环中删除元素会导致`ConcurrentModificationException`异常,原因是for each循环底层使用了Iterator,而Iterator在遍历期间不允许集合修改。相比之下,使用Iterator的remove方法能正确删除元素,因为它更新了内部状态以适应集合的变化。文章提供了参考文献以深入理解源码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Iterator介绍

  对于java中的集合类(Collection),可以使用迭代器Iterator对集合中的元素进行遍历。迭代器是一种设计模式,它可以在不暴露集合中元素的情况下而去遍历集合中的所有元素。
  Iterator为一个接口,只定义了三个方法,hasNext(),next(),和remove()。Collection接口继承Iterable接口,提供了一个iterator()方法,使得Collection子类通过iterator()方法获取Collection内部实现的Iterator对象。

2. for循环删除容器元素误区

  正常来说,如果我们需要删除容器中某个特定元素,直接想到的方法就是使用for循环遍历,拿下面例子来说:

	List<String> list = new ArrayList<>();
	list.add("a");
	list.add("b");
	list.add("c");
    list.add("d");
	
	for (String str : list) {
            if ("b".equals(str))
                list.remove(str);
            else
                System.out.println(str);
        }
    System.out.println(list);

此时允许代码会抛出如下异常:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)

查看859行代码和909行代码:

858        public E next() {
859          checkForComodification();
...          ...	
        }
...
907       final void checkForComodification() {
908          if (modCount != expectedModCount)
909             throw new ConcurrentModificationException();
...     }

  next()是ArrayList内部实现Iterator的方法,改方法在859行调用了checkForComdification方法。改方法在909行为什么抛出异常呢?原因在于list执行remove操作的时候,"b"的位置已经被"c"取代,list上的元素做了移动(具体可以查看ArrayList的remove源码),而remove操作导致了modCount++,这时迭代器中的等号已经不成立了,故抛出异常。

  综上,原因就在于Java中的for each实际上使用的是iterator进行处理的。而iterator是不允许集合在iterator使用期间删除的。所以导致了iterator抛出了ConcurrentModificationException

  如果对于for each还是不甘心的,可以使用一个容器将删除元素先暂存起来,然后执行完遍历后,使用removeAll操作也可以达到预期效果。

3. Iterator迭代器删除

先来看下使用迭代器删除例子:

	List<String> list = new ArrayList<>();
	list.add("a");
	list.add("b");
	list.add("c");
    list.add("d");
	
	while (iter.hasNext()) {
       String str = iter.next();
       if ("b".equals(str))
          iter.remove();
       else
          System.out.println(str);
    }
    System.out.println(list);

程序正常运行输出:

a
c
d
[a, c, d]

这里之所以不会和for each报相同错误得益于它的remove方法,查看Iterator的迭代器remove源码:

public void remove() {
     if (lastRet < 0)
         throw new IllegalStateException();
     checkForComodification();

     try {
         ArrayList.this.remove(lastRet);
         cursor = lastRet;
         lastRet = -1;
         expectedModCount = modCount;
     } catch (IndexOutOfBoundsException ex) {
         throw new ConcurrentModificationException();
     }
}

会发现,Iterator在使用ArrayList自身的remove方法之后,执行了expectedModCount = modCount,这也就保证了Iterator在执行next的方法等时候两者的等号是成立的。

4. 参考文献

【注⚠️】从源码中学习,收获会更大!

  • https://blog.csdn.net/qiyei2009/article/details/51945883?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
  • https://blog.csdn.net/sinat_35495823/article/details/68922426?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-2.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-2.control
  • https://blog.csdn.net/qq_39949109/article/details/80415776?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-1&spm=1001.2101.3001.4242
  • https://blog.csdn.net/zhao123h/article/details/51271482?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值