List集合删除元素的正确姿态

一、集合元素遍历

在遍历集合的时候,我们可能会根据业务的需要而需要排除集合的一些元素,通常我们遍历List集合常用的有下列3种方式:

1.普通for循环


forint i= 0; i<list.size(); i++) {
    
    
}

2.增强for

forint i : list){
    
    
}

3.迭代器

Iterator <Integer> iterator = list.iterator();
while (iterator.hasNext()){
    
    }

二、集合元素的删除

综合上面三种方式测试删除,代码如下:

 @Test
    public void test3(){
    
    

        try {
    
    
            delete1();
        }catch (Exception e){
    
    
            log.info("delete1 方法遍历删除出现异常,异常类型是{}",e.getClass().getSimpleName());
        }
        try {
    
    
            delete2();
        }catch (Exception e){
    
    
            log.info("delete2 方法遍历删除出现异常,异常类型是{}",e.getClass().getSimpleName());
        }
        try {
    
    
            delete3();
        }catch (Exception e){
    
    
            log.info("delete3 方法遍历删除出现异常,异常类型是{}",e.getClass().getSimpleName());
        }

    } 
    // 初始化一个list 
    public List<Integer> init(){
    
    
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        return  list;
    }
    // 普通for循环删除
    public <T> void  delete1(){
    
    
        List<Integer> list = init();
        for (int i = 0; i < list.size(); i++) {
    
    
            if (list.get(i).equals(3)){
    
    
                list.remove(i);
            }
        }

        log.info("delete1 方法执行成功");
    }
	// 迭代器删除
    public void  delete2(){
    
    
        List<Integer> list = init();
        Iterator <Integer> iterator = list.iterator();
        while (iterator.hasNext()){
    
    
            Integer next = iterator.next();
            if (next.equals(3)) {
    
    
                iterator.remove();
            }
        }
        log.info("delete2 方法执行成功");
    }
    // 增强for循环
    public  void  delete3(){
    
    
        List<Integer> list = init();
        for (Integer t : list) {
    
    
            if (t.equals(3)) {
    
    
                list.remove(t);
            }
        }
        log.info("delete3 方法执行成功");
    }

我们来看一下控制台输出的结果:

21:03:01.697 [main] INFO com.kuake.concurrent.DemoTest - delete1 方法执行成功
21:03:01.701 [main] INFO com.kuake.concurrent.DemoTest - delete2 方法执行成功
21:03:01.701 [main] INFO com.kuake.concurrent.DemoTest - delete3 方法遍历删除出现异常,异常类型是ConcurrentModificationException

三、测试结果

>普通for删除: 删除正常
>增强for删除: 删除时抛出异常ConcurrentModificationException
>迭代器删除: 删除正常

四、异常探究

为什么使用增强for循环会抛出这个异常呢。首先我来看看增强for遍历编译之后的代码是怎么样。通过idea找到对应的class文件。

 public void delete3() {
    
    
        List<Integer> list = this.init();
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
    
    
            Integer t = (Integer)var2.next();
            if (t.equals(3)) {
    
    
                list.remove(t);
            }
        }

        log.info("delete3 方法执行成功");
    }

原来我们的迭代器,在编译之后,jvm也是把它翻译成了使用迭代器进行遍历。那么我们就可以怀疑是不是迭代器遍历的时候有什么要求呢?当翻阅ArrayList的迭代器源码时,看到了如下代码。代码片段如下:这是他的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 是表示list集合的修改次数,expectedModCount是合理操作时的逾期修改次数;正常操作时modCount==expectedModCount

**原因:**是因为list.remove()方法会使得modCount++操作;而expectedModCount保存的还是原始值。然后当执行迭代器的 next()操作时就会抛出上述异常。

五、迭代器的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();
            }

迭代器的remove方法 会将执行expectedModCount = modCount操作。所以在下一次执行next()方法就不会抛出异常。

六、小结

推荐使用 迭代器的remove()方法进行集合元素删除操作。

猜你喜欢

转载自blog.csdn.net/weixin_43732955/article/details/105908733
今日推荐