博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中ArrayList的删除元素方法总结
阅读量:4093 次
发布时间:2019-05-25

本文共 2919 字,大约阅读时间需要 9 分钟。

Java中循环遍历元素,一般有for循环遍历,foreach循环遍历,iterator遍历。

举例

定义一个List对象

List
list = new ArrayList<>();list.add("1");list.add("2");list.add("3");

一、普通for循环遍历

for (int i = 0; i < list.size(); i++) {    System.out.println(i + ":" + list.get(i));    String s = list.get(i);    if ("1".equals(s)) {        list.remove(s);    }}System.out.println(list);

输出结果为

0:11:3[2, 3]

这种删除方法明显有问题,遗漏了被删除元素的下一个元素。

这种情况下,如果被删除元素切好是List中最后一个元素,则输出结果恰好正常。
解决方法:
遗漏元素是因为删除元素后,List的size已经减1,但i不变,则i位置元素等于被跳过,不在循环中处理。
若if代码块中调用remove函数后,加上i–,则能避免这种错误。

二、Iterator遍历

Iterator
iterator = list.iterator(); while (iterator.hasNext()){ String str = iterator.next(); System.out.println(str); if("2".equals(str)) { iterator.remove(); }}System.out.println(list);

输出结果为

123[1, 3]

最安全的遍历中删除元素方法。

借用了Iterator,删除元素用的也是Iterator的remove方法,而不是List中的。

三、foreach循环遍历

for (String s : list) {    System.out.println(s);    if ("2".equals(s)) {        list.remove(s);    }}

删除元素2:正常输出

1  2  [1, 3]

删除元素1或3:报错

java.util.ConcurrentModificationException  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)  at java.util.ArrayList$Itr.next(ArrayList.java:859)

foreach的本质

通过反编译,foreach的代码实现如下:

Iterator itr3 = list.iterator();while(itr3.hasNext()) {    String s = (String)itr3.next();    System.out.println(s);    if ("2".equals(s)) {        list.remove(s);    }}

对比后发现,foreach实质上也是使用Iterator进行遍历。

不同的地方在于,一个使用Iterator的删除方法,一个使用List的删除方法。
问题出在 list.remove(s); 代码中。我们查看一下ArrayList的报错相关代码。代码如下:

public boolean hasNext() {    return cursor != size;}public E next() {    checkForComodification();//859行    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];}final void checkForComodification() {    if (modCount != expectedModCount)        throw new ConcurrentModificationException();//909行}public boolean remove(Object o) {    ···    fastRemove(index);    ···}private void fastRemove(int index) {    modCount++;    ···}

其中,size和modCount为ArrayList属性;cursor和expectedModCount为ArrayList.Iterator属性。

  • size: List长度
  • modCount: List在结构上被修改的次数
  • cursor: Iterator中下一个被返回的元素的下标
  • expectedModCount: 属于ArrayList.Iterator,与modCount类似,初始化值等于modCount值

输出分析

  1. 报错是因为remove方法改变了modCount,导致next方法时checkForComodification检查不通过,抛出异常。
  2. 移除2时正常:因为2刚好是倒数第二个元素,移除后size-1,在hasNext方法中已结束循环,不在调用next方法。虽然不报错,但会使最后一个元素被跳过,没有进入循环。
  3. 移除1或3失败略有不同:remove(3)后,size减1,cursor已经比size大1,但由于hasNext方法是 cursor!=size,还是会进入循环,在next方法中才会报错。如果hasNext方法是 cursor>size ,移除3的情形会类似于移除2(不报错,直接退出进入循环)。

四、结论及其他

  1. 集合中遍历移除元素保险起见都是使用Iterator,这没什么好争议的。
  2. Java8中的删除方法removeIf,其实也是使用Iterator。
list.removeIf(e->e.equals("2"));
  1. Java8中使用如下方式删除,本质上是new了一个List,结果已经不是原List了。类似的,上述的遍历中,new一个新的List,将需要的元素add进入也是可行的。
list = list.stream().filter(l->!l.equals("2")).collect(Collectors.toList());

转载博客:

你可能感兴趣的文章
处理Maven本地仓库.lastUpdated文件
查看>>
Kafka | 请求是怎么被处理的?
查看>>
Java并发编程1-线程池
查看>>
CentOS7,玩转samba服务,基于身份验证的共享
查看>>
计算机网络-网络协议模型
查看>>
计算机网络-OSI各层概述
查看>>
Java--String/StringBuffer/StringBuilder区别
查看>>
mySQL--深入理解事务隔离级别
查看>>
分布式之redis复习精讲
查看>>
数据结构与算法7-栈
查看>>
线性数据结构学习笔记
查看>>
数据结构与算法14-跳表
查看>>
Java并发编程 | 一不小心就死锁了,怎么办?
查看>>
(python版)《剑指Offer》JZ01:二维数组中的查找
查看>>
(python版)《剑指Offer》JZ06:旋转数组的最小数字
查看>>
(python版)《剑指Offer》JZ13:调整数组顺序使奇数位于偶数前面
查看>>
(python版)《剑指Offer》JZ28:数组中出现次数超过一半的数字
查看>>
(python版)《剑指Offer》JZ30:连续子数组的最大和
查看>>
(python版)《剑指Offer》JZ32:把数组排成最小的数
查看>>
(python版)《剑指Offer》JZ02:替换空格
查看>>