Java中如何正确遍历删除List中的元素

 更新时间:2023年11月21日 11:21:07   作者:程序员子龙  
删除List中元素这个场景很场景,很多人可能直接在循环中直接去删除元素,这样做对吗?下面小编就来和大家一起讨论如何正确遍历删除List中的元素,文中有详细的代码示例供大家参考,需要的朋友可以参考下

for循环索引删除

删除长度为4的字符串元素。

    List<String> list = new ArrayList<String>();
    list.add("AA");
    list.add("BBB");
    list.add("CCCC");
    list.add("DDDD");
    list.add("EEE");
​
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).length() == 4) {
            list.remove(i);
        }
    }
    System.out.println(list);
}

实际上输出结果:

[AA, BBB, DDDD, EEE]

DDDD 竟然没有删掉!

原因是:删除某个元素后,list的大小size发生了变化,而list的索引也在变化,索引为i的元素删除后,后边元素的索引自动向前补位,即原来索引为i+1的元素,变为了索引为i的元素,但是下一次循环取的索引是i+1,此时你以为取到的是原来索引为i+1的元素,其实取到是原来索引为i+2的元素,所以会导致你在遍历的时候漏掉某些元素。

比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。不会报出异常,只会出现漏删的情况。

foreach循环删除元素

for (String s : list) {
        if (s.length() == 4) {
            list.remove(s);
​
        }
    }
    System.out.println(list);

如果没有break,会报错:

java.util.ConcurrentModificationException at java.util.ArrayListItr.checkForComodification(ArrayList.java:911)atjava.util.ArrayListItr.checkForComodification(ArrayList.java:911) at java.util.ArrayListItr.checkForComodification(ArrayList.java:911)atjava.util.ArrayListItr.next(ArrayList.java:861) at com.demo.ApplicationTest.testDel(ApplicationTest.java:64) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

报ConcurrentModificationException错误的原因:

看一下JDK源码中ArrayList的remove源码是怎么实现的:

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

一般情况下程序会最终调用fastRemove方法:

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; // clear to let GC do its work
    }

在fastRemove方法中,可以看到第2行把modCount变量的值加一,但在ArrayList返回的迭代器会做迭代器内部的修改次数检查:

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

而foreach写法是对实际的Iterable、hasNext、next方法的简写,因为上面的remove(Object)方法修改了modCount的值,所以才会报出并发修改异常。

阿里开发手册也明确说明禁止使用foreach删除、增加List元素。

迭代器Iterator删除元素

    Iterator<String> iterator = list.iterator();
    while(iterator.hasNext()){
        if(iterator.next().length()==4){
            iterator.remove();
        }
    }
    System.out.println(list);

[AA, BBB, EEE]

这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,而不是List的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。

总结

无论什么场景,都不要对List使用for循环的同时,删除List集合元素,要使用迭代器删除元素。

到此这篇关于Java中如何正确遍历删除List中的元素的文章就介绍到这了,更多相关Java遍历删除List内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现调用对方http接口得到返回数据

    Java实现调用对方http接口得到返回数据

    这篇文章主要介绍了Java实现调用对方http接口得到返回数据,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java中关于 null 的几种处理方式详解

    Java中关于 null 的几种处理方式详解

    这篇文章主要介绍了Java中关于 null 的几种处理方式,关于 null ,你应该知道下面这几件事情来有效的了解 null ,从而避免很多由 null 引起的错误,具体细节跟随小编一起学习下吧
    2021-10-10
  • java实现邮件发送

    java实现邮件发送

    这篇文章主要为大家详细介绍了java实现邮件发送,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • IP查询系统的异步回调案例

    IP查询系统的异步回调案例

    本文主要分享了IP查询系统的异步回调案例,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • 浅谈Java中各种修饰符与访问修饰符的说明

    浅谈Java中各种修饰符与访问修饰符的说明

    下面小编就为大家带来一篇浅谈Java中各种修饰符与访问修饰符的说明。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Java SPI的简单小实例

    Java SPI的简单小实例

    这篇文章主要介绍了Java SPI的简单小实例,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • Java高级特性基础之反射五连问

    Java高级特性基础之反射五连问

    反射赋予了我们在运行时分析类以及执行类中方法的能力。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。本文就来和大家详细聊聊Java中的反射,感兴趣的可以了解一下
    2023-01-01
  • Java基础之JDK1.8新特性lambda表达式详解

    Java基础之JDK1.8新特性lambda表达式详解

    函数式接口有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,函数式接口可以被隐式转换为lambda表达式,这篇文章主要介绍了Java基础之lambda表达式(JDK1.8新特性),需要的朋友可以参考下
    2023-08-08
  • java 实现取int型的第二个字节的数

    java 实现取int型的第二个字节的数

    这篇文章主要介绍了java 实现取int型的第二个字节的数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • 浅谈mybatis如何半自动化解耦(推荐)

    浅谈mybatis如何半自动化解耦(推荐)

    这篇文章主要介绍了浅谈mybatis如何半自动化解耦,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06

最新评论