java list中删除元素用remove()报错的fail-fast机制原理以及解决方案

原文:https://blog.csdn.net/lululove19870526/article/details/70808903       

 java list中删除元素用remove()报错的fail-fast机制原理以及解决方案

  现在有一个list,有6个元素,值分别是1、5、5、8、5、10,现需要删除值为5的元素

  第一种

           

import java.util.ArrayList;
import java.util.List;
 
public class ListRemove1 {
    
     public static void main(String args[]) {
            List<Integer> list = new ArrayList<Integer>();
            list.add(1);
            list.add(5);
            list.add(5);
            list.add(8);
            list.add(5);
            list.add(10);
 
            for (int j = 0; j < list.size(); j++) {
                if (list.get(j) == 5) {
                    list.remove(j);
                }
            }
 
            outputList(list);
        }
 
        private static void outputList(List<Integer> list) {
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)+"    ");
            }
        }
 
}
运行结果
     1    5    8    10  
   这结果显然不是我们的预期,我们是希望删除List中所有为5的元素,但输出结果中却出现了5,这是因为在i等于1时,删除了List中的index为1的元素5,这时候list为[1,5,8,5,10], 但接下来,i递增后,等于2,在list.get(i)时,取出来的结果就成为了8了,也就是说随着list元素的删除,index是随之变化的,这就是其中的陷阱

第二种

    

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
 
public class ListRemove2 {
    
     public static void main(String args[]) {
            List<Integer> list = new ArrayList<Integer>();
            list.add(1);
            list.add(5);
            list.add(5);
            list.add(8);
            list.add(5);
            list.add(10);
           
            /*
            for (int i1 : list) {
                if (i1 == 5) {
                    list.remove(i1);
                }
            }
            */
            
            Iterator<Integer>  iterator = list.iterator();
            while(iterator.hasNext()){
                if(iterator.next() == 5){
                    list.remove(5);
                }
            }
 
            outputList(list);
        }
 
        private static void outputList(List<Integer> list) {
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)+"    ");
            }
        }
 
}
 上面代码不管用增强for循环还是用Iterator遍历list,用list.remove()方法都报下面的错误:


  

   之所以报上面的错误是因为发生了fail-fast 事件。

       fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。

   1.报ConcurrentModificationException异常的原因

     ConcurrentModificationException是在操作Iterator时抛出的异常,ArrayList的Iterator是在父类AbstractList.java中实现的,具体源码:

    

    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
 
 
        public Iterator<E> iterator() {
        return new Itr();
        }
 
        private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;
 
        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;
 
        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;  
 
        public boolean hasNext() {
                return cursor != size();
        }
 
        public E next() {
                checkForComodification();
            try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
            } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
            }
        }
 
        public void remove() {
            if (lastRet == -1)
            throw new IllegalStateException();
                checkForComodification();
 
            try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
            }
        }
 
        final void checkForComodification() {
            if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        }
        }
 
        protected transient int modCount = 0;
    }

    ArrayList中的remove()的实现

    

    public class ArrayList<E> extends AbstractList<E> implements List<E>,
            RandomAccess, Cloneable, java.io.Serializable {
        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;
        }
 
        /*
         * Private remove method that skips bounds checking and does not return
         * the value removed.
         */
        private void fastRemove(int index) {
            modCount++;
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index + 1, elementData, index,
                        numMoved);
            elementData[--size] = null; // Let gc do its work
        }
    }
      无论是add()、remove(),还是clear(),只要涉及到修改集合中的元素个数时,都会改变modCount的值
      从源码中我们可以看出:

          1) Iterator<Integer>  iterator = list.iterator();执行这语句时,此时Iterator中的expectedModCount = modCount(因为list有6个元素,所有该值为6),

          2)遍历到第二个元素即值为5的时候,list.remove(5);从ArrayList中的remove()实现中发现:modCount++;此时modCount的值为7

          3)在执行if(iterator.next() == 5)这语句即调用iterator的next()方法时,next()方法实现会调用checkForComodification();,该方法会执行 if (modCount != expectedModCount),即比较modCount和expectedModCount的值是否相等,此时expectedModCount的值为6而modCount的值为7,这两个值不相等,所以抛出

throw new ConcurrentModificationException();这个异常

    第三种即解决方案:调用iterator的iterator.remove()方法,该方法的实现中,会执行expectedModCount = modCount;这个语句,即expectedModCount赋值为modCount;

这样就保证expectedModCount 等于modCount,不会抛出ConcurrentModificationException()这个异常

     

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
 
public class ListRemove4 {
    
     public static void main(String args[]) {
            List<Integer> list = new ArrayList<Integer>();
            list.add(1);
            list.add(5);
            list.add(5);
            list.add(8);
            list.add(5);
            list.add(10);
 
            Iterator<Integer>  iterator = list.iterator();
            while(iterator.hasNext()){
                if(iterator.next() == 5){
                    iterator.remove();
                }
            }
 
            outputList(list);
        }
 
        private static void outputList(List<Integer> list) {
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)+"    ");
            }
        }
 
 
}
运行结果:
     1    8    10 
--------------------- 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值