遍历ArrayList时同时修改引发的问题

看见一篇博客,没有写完整,于是增补了一下:

博客原文:http://www.cnblogs.com/alipayhutu/archive/2012/08/11/2634073.html

注:黄色字体为我添的

CopyOnWriteArrayList,因何而存在?

ArrayList的一个线程安全的变体,其所有可变操作(addset 等)都是通过对底层数组进行一次新的复制来实现的,代价昂贵。

CopyOnWriteArrayList,是因”并发”而生。

 

【场景一】对于ArrayList,使用直接方式,一边遍历,一边删除,会报错。

 // 删除/修改元素
 for(String item : list){
     list.remove(item);
 }

 

 

image

原因:foreach语法是通过Iterator来实现的,当遍历这个List的时候,会生成一个ArrayList.Itr对象,这个私有内部类实现了Iterator接口,也就是说上面这段代码与下面这段代码效果一样:

  Iterator it = list.iterator() ;

  while(it.hasNext()){

    String temp = it.next() ;

    list.remove(temp) ; 

  }

在ArrayList中有一变量记录的当前这个ArrayList被修改的次数,每当调用add/remove方法就会把该参数的值加一,当生成Iterator对象时该对象会记录当前状态ArrayList的修改次数,然后在每次调用it.next()时就会判断当前ArrayList修改次数是否和它记录的相同,如果不同就抛出异常。例如上面等效后的代码,当执行 it = list.iterator() ;时 it对象记录在执行这句代码之前list对象的修改次数,当第一次执行it.next()语句时不会发生异常,因为这时list还没有被修改,但是当第二次执行it.next()时list已经通过list.remove(temp)代码修改了其内部的修改次数变量,所以导致it对象记录的修改次数和list的修改次数不同,所以就抛出了异常。

只需记住的准则是: 使用Iterator遍历集合时是不能修改集合的。

雨伞 解决办法一:使用迭代器,一边遍历,一边删除,不会报错。

// 删除/修改元素 
Iterator<String> it = list.iterator(); 
while(it.hasNext()){ 
    String ele = it.next(); 
    it.remove(); 

http://blog.sina.com.cn/s/blog_605f5b4f0100qsgf.html

使用Iterator的remove方法时不会对集合是否被更改进行判断,所以上面不会出现ModifyException。

雨伞 解决办法二:使用CopyOnWriteArrayList,直接方式,一边遍历,一会删除,不会报错。

for(String item : list){ 
    list.remove(item); 

 使用CopyOnWriteArrayList的remove方法时,该方法会先将List复制一份,对副本进行修改,然后把以前的List引用重新指向副本,所以不会出现异常。

 

【场景二】对于ArrayList,使用迭代器,一边遍历,一边add,会报错。

Iterator<String> it = list.iterator(); 
while(it.hasNext()){ 
    String str = it.next(); 
    String tem = str + "..."; 
    list.add(tem); 

原因:因为list.add会修改其内部存储的list修改次数变量,导致it内部记录的list修改次数和list当前状态的修改次数不同,所以产生异常。

雨伞 解决办法一:改用CopyOnWriteArrayList,直接方式,一边遍历,一边add,不会报错。

for(String item : list){ 
    String tem = item + "..."; 
    list.add(tem); 

 

使用CopyOnWriteArrayList的add方法时,该方法会先将List复制一份,对副本进行修改,然后把以前的List引用重新指向副本,所以不会出现异常,

直接遍历与使用Iterator的效果是一样的。

雨伞 解决办法二:改用CopyOnWriteArrayList,一边遍历,一边add,不会报错。

        Iterator<String> it = list.iterator(); 
        while(it.hasNext()){ 
            String str = it.next(); 
            String tem = str + "..."; 
            list.add(tem); 
        }

 

【场景三】对于CopyOnWriteArrayList,迭代器,不能remove。

Iterator<String> it = list.iterator(); 
while(it.hasNext()){ 
    String str = it.next(); 
    String tem = str + "..."; 
    it.remove(); 

CopyOnWriteArrayList实现的remove方法中直接抛出异常,说明不支持该操作,至于为什么这样做还待以后慢慢体会。

image

雨伞 解决办法一:使用直接方式,一边遍历,一边add/remove()

for(String item : list){ 
    String tem = item + "..."; 
    list.remove(item); 
    list.add(tem); 

 上面这个解决方案作者写错了,这段代码会抛出ModifyException异常。

【总结】底层究竟发生着什么?CPU、内存使用如何? to be continued…

转载于:https://www.cnblogs.com/caiyao/p/4963206.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Arraylist和HashMap在遍历候插入数据会引起并发修改异常,因为它们两个都是不同步的,当其他线程在迭代器上修改元素,会造成冲突。因此,如果要在遍历候插入元素,建议使用线程安全的集合,比如CopyOnWriteArrayList和ConcurrentHashMap。 ### 回答2: 当ArrayList和HashMap正在遍历插入元素会导致两种不同的问题。 对于ArrayList来说,当我们在遍历ArrayList插入元素,会导致ConcurrentModificationException(并发修改异常)的抛出。这是因为ArrayList遍历使用一个迭代器(Iterator)来获取元素,并且在每次调用迭代器的next()方法都会检查modCount(修改次数)是否与预期一致,如果不一致则抛出异常。而插入元素会增加modCount的值,导致modCount与预期不一致,从而引发异常。 对于HashMap来说,当我们在遍历HashMap插入元素,不会抛出ConcurrentModificationException异常,但会导致遍历过程出现不可预测的结果,可能会漏掉一些元素或者重复遍历某些元素。这是因为HashMap的遍历过程是通过entrySet()方法返回的迭代器来实现的,而且插入元素可能会导致HashMap的结构发生变化,从而影响迭代器的操作。在HashMap的实现中,如果在遍历过程中发现结构发生变化,会通过fail-fast机制立刻抛出ConcurrentModificationException异常,但并不是所有情况下都能及检测到结构变化。 因此,在遍历ArrayList和HashMap,最好避免在遍历过程中插入元素,可以通过在遍历前或者遍历后进行元素的插入操作,或者选择使用线程安全的数据结构来避免这个问题。 ### 回答3: 在使用ArrayList和HashMap,如果在遍历的过程中进行插入操作,可能会导致遍历出现问题。 对于ArrayList,当我们使用迭代器或者for循环进行遍历,如果在遍历过程中进行插入操作,可能会导致ConcurrentModificationException(并发修改异常)的出现。这是因为ArrayList在进行插入操作,会修改内部的modCount(修改次数)属性,而迭代器或者for循环会在每次迭代检查modCount的值是否一致,如果不一致则抛出该异常。 对于HashMap,同样在使用迭代器或者for-each循环进行遍历,如果在遍历过程中进行插入操作,可能会导致ConcurrentModificationException的出现。HashMap的遍历是基于Entry数组实现的,当进行插入操作,会导致数组的大小发生改变或者原有的元素位置发生改变。而迭代器或者for-each循环在每次迭代,都会通过比较modCount判断HashMap是否发生了结构性的改变,如果发生了改变则抛出该异常。 因此,在遍历ArrayList和HashMap,如果要进行插入操作,可以考虑使用Iterator的add方法或者使用扩展增强for循环,它们能够避免ConcurrentModificationException的异常出现。另外,如果需要并发操作,可以考虑使用并发集合类如CopyOnWriteArrayList和ConcurrentHashMap来避免出现并发修改异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值