Java源码分析(一)-- modCount与ConcurrentModficationException异常

本文深入分析了Java ArrayList中的`modCount`及其引发的`ConcurrentModficationException`异常。在使用Iterator遍历ArrayList时,如果直接修改列表,会导致此异常。通过源码解析,阐述了异常产生的原因和解决方法,提醒开发者避免在遍历过程中修改集合。

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

前言

我在阅读Java.util.ArrayList源码的时候,发现不少方法中有”modCount++;”,很好奇这个modCount有何作用,一番查证,找到了一些有趣的东西
参考:https://blog.youkuaiyun.com/androiddevelop/article/details/21509345

摘要

modCount 记录修改次数,
ConcurrentModficationException 并发修改异常,
通过Iterator遍历过程中,如果对修改ArrayList进行修改,可能会引发”并发修改异常”,

这是操作的不规范,让我带大家跳坑,详细分析这个错误,死而后已。


一、错误示范,First Blood.

public static void main(String[] args){
    ArrayList<Integer> list = new ArrayList<Integer>();  
    list.add(3);  
    Iterator<Integer> iterator = list.iterator();  
    while(iterator.hasNext()){  
       Integer integer = iterator.next();  
       if(integer==3)  
            list.remove(integer);   //注意这个地方  
    } 
}

重点来了,这份代码的报错,可不是IndexOutOfBounds越界操作,而是所谓的ConcurrentModficationException并发修改异常,如下图:

这里写图片描述

让我们撕开她的外衣,看看里面的东西吧


二、 ConcurrentModfication异常分析

前面和数组越界异常进行比较,事实上,这个异常与Iterator迭代器有关,是迭代器出问题时的异常。

对于上面这个例子来说,是在下面这个地方进入了异常。

while(iterator.hasNext()){
    Integer integer = iterator.next();  
    ...
}

我们观察ArrayList的hasNext()代码:

public boolean hasNext() {
    return cursor != size;
}

list本来有一个元素,删除过后size为0,但此时迭代器游标cursor为1,查看hasNext(),当然不相等。继续循环!!(ps:如果因为删除而cursor==size,会安全退出,但实际并非我们想要的结果)

然后遇见了next()

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];
}

checkForComodification()

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

没错,创建迭代器的时候会将当时的modCount赋值给expectedModCount,靠这个来判断是否遍历时进行了修改操作。


三、 如何解决

 // 1 使用Iterator提供的remove方法,用于删除当前元素  
 for (Iterator<String> it = myList.iterator(); it.hasNext();) {  
     String value = it.next();  
      if (value.equals( "3")) {  
          it.remove();  // ok  
     }  
}  
System. out.println( "List Value:" + myList.toString());  

 // 2 建一个集合,记录需要删除的元素,之后统一删除               
List<String> templist = new ArrayList<String>();  
 for (String value : myList) {  
      if (value.equals( "3")) {  
          templist.remove(value);  
     }  
}  
 // 可以查看removeAll源码,其中使用Iterator进行遍历  
myList.removeAll(templist);  
System. out.println( "List Value:" + myList.toString());          

  // 3. 使用线程安全CopyOnWriteArrayList进行删除操作  
List<String> myList = new CopyOnWriteArrayList<String>();  
myList.add( "1");  
myList.add( "2");  
myList.add( "3");  
myList.add( "4");  
myList.add( "5");  

Iterator<String> it = myList.iterator();  

 while (it.hasNext()) {  
     String value = it.next();  
      if (value.equals( "3")) {  
          myList.remove( "4");  
          myList.add( "6");  
          myList.add( "7");  
     }  
}  
System. out.println( "List Value:" + myList.toString());  

 // 4. 不使用Iterator进行遍历,需要注意的是自己保证索引正常  
 for ( int i = 0; i < myList.size(); i++) {  
     String value = myList.get(i);  
     System. out.println( "List Value:" + value);  
      if (value.equals( "3")) {  
          myList.remove(value);  // ok  
          i--; // 因为位置发生改变,所以必须修改i的位置  
     }  
}  
System. out.println( "List Value:" + myList.toString());  

在遍历时的同时进行操作,本身就是一个非常危险的行为,这个并发修改异常就是为了基于此的一种保护。所以,我们要做的,不是寻找方法突破这个保护机制,而更应该在程序编写上规避这类行为。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值