Java基础系列(面试必备):Java循环删除List元素正确方法!
前言
今天博主将为大家分享:Java基础系列(面试必备):Java循环删除List元素的正确方法!不喜勿喷,如有异议欢迎讨论!
上代码
贴出代码各位可以复制去试一下,有两个正确的删除方式和一些常见的错误方式注意看哦!
package com.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* @Description: List的正确删除remove的姿势
* @ClassName: ListRemoveTest.java
* @author ChenYongJia
* @Date 2019年7月3日 12:25
* @Email chen87647213@163.com
*/
public class ListRemoveTest {
public static void main(String[] args) {
/**
* 先声明一个集合
*/
List<String> list = new ArrayList<>(Arrays.asList("c1", "cy2", "c3", "cy4", "c5", "cy6", "c7", "cy8", "c9"));
/**
* 正确的删除方式
* 通过 CopyOnWriteArrayList 解决了 List的并发问题。每次remove的都是复制出的list
*/
final CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<String>(list);
for (String item : cowList) {
if (item.equals("y")) {
cowList.remove(item);
}
}
System.out.print(cowList+"\n");
/**
* 报错 java.util.ConcurrentModificationException
*/
for (String str : list) {
if (str.contains("y")) {
list.remove(str);
}
}
System.out.print(list+"\n");
/**
* 报错:下标越界 java.lang.IndexOutOfBoundsException
*/
int size = list.size();
for (int i = 0; i < size; i++) {
String str = list.get(i);
if (str.contains("y")) {
list.remove(i);
}
}
System.out.print(list+"\n");
/**
* 正常删除,每次调用size方法,损耗性能,不推荐
*/
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
if (str.contains("y")) {
list.remove(i);
}
}
System.out.print(list+"\n");
/**
* 正常删除,推荐使用
*/
for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
String str = ite.next();
if (str.contains("y")) {
ite.remove();
}
}
System.out.print(list+"\n");
/**
* 报错 java.util.ConcurrentModificationException
*/
for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
String str = ite.next();
if (str.contains("y")) {
list.remove(str);
}
}
System.out.print(list+"\n");
}
}
一下解释会对应上述代码
- for循环遍历list
这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
- 增强for循环
这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。
- iterator遍历
这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误,所以使用iterator的remove方法才是正解。
- 小结
(1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。
(2)循环删除list中多个元素的,应该使用迭代器iterator方式。
继续分析
报异常IndexOutOfBoundsException我们很理解,是动态删除了元素导致数组下标越界了。
- 那ConcurrentModificationException呢?
其中,for(xx in xx)是增强的for循环,即迭代器Iterator的加强实现,其内部是调用的Iterator的方法,为什么会报ConcurrentModificationException错误,我们来看下源码
-
通过代码我们发现 Itr 是 ArrayList 中定义的一个私有内部类,
-
在 next、remove方法中都会调用 checkForComodification 方法,该方法的作用是判断 modCount != expectedModCount是否相等,如果不相等则抛出ConcurrentModificationException异常。
-
每次正常执行 remove 方法后,都会对执行expectedModCount = modCount赋值,保证两个值相等,那么问题基本上已经清晰了,在 foreach 循环中执行 list.remove(item);,对 list 对象的 modCount 值进行了修改,而 list 对象的迭代器的expectedModCount 值未进行修改,因此抛出了ConcurrentModificationException异常。
取下个元素的时候都会去判断要修改的数量和期待修改的数量是否一致,不一致则会报错,而通过迭代器本身调用remove方法则不会有这个问题,因为它删除的时候会把这两个数量同步,而list则不然。搞清楚它是增加的for循环就不难理解其中的奥秘了。
List源码如下
到这里:Java基础系列(面试必备):Java循环删除List元素的正确方法!分享完毕了,快去试试吧!
最后
-
更多参考精彩博文请看这里:《陈永佳的博客》
-
喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/97795.html