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

  • 先定义一个List对象
List<String> 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);
// i--;
}
}
System.out.println(list);

输出结果为

0:1
1:3
[2, 3]

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

这种情况下,如果被删除元素切好是List中最后一个元素,则输出结果恰好正常。

解决方法:

遗漏元素是因为删除元素后,List的size已经减1,但i不变,则i位置元素等于被跳过,不在循环中处理。

若if代码块中调用remove函数后,加上i--,则能避免这种错误。

二、Iterator遍历

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

输出结果为

1
2
3
[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.Itr属性。

size: List长度
modCount: List在结构上被修改的次数
cursor: Itr中下一个被返回的元素的下标
expectedModCount: 属于ArrayList.Itr,与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"));
  3. Java8中使用如下方式删除,本质上是new了一个List,结果已经不是原List了。类似的,上述的遍历中,new一个新的List,将需要的元素add进入也是可行的。
    list = list.stream().filter(l->!l.equals("2")).collect(Collectors.toList());

Java中ArrayList的删除元素总结的更多相关文章

  1. java中List遍历删除元素-----不能直接 list.remove()

    https://blog.csdn.net/github_2011/article/details/54927531 这是List接口中的方法,List集合调用此方法可以得到一个迭代器对象(Itera ...

  2. java中ArrayList 、LinkList区别

    转自:http://blog.csdn.net/wuchuanpingstone/article/details/6678653 个人建议:以下这篇文章,是从例子说明的方式,解释ArrayList.L ...

  3. JAVA中ArrayList用法

    JAVA中ArrayList用法 2011-07-20 15:02:03|  分类: 计算机专业 |  标签:java  arraylist用法  |举报|字号 订阅     Java学习过程中做题时 ...

  4. Java中ArrayList与LinkedList的区别

    Java中ArrayList与LinkedList的区别 一般大家都知道ArrayList和LinkedList的区别: 1. ArrayList的实现是基于数组,LinkedList的实现是基于双向 ...

  5. Java中arraylist和linkedlist源代码分析与性能比較

    Java中arraylist和linkedlist源代码分析与性能比較 1,简单介绍 在java开发中比較经常使用的数据结构是arraylist和linkedlist,本文主要从源代码角度分析arra ...

  6. Java中ArrayList和LinkedList的性能分析

    ArrayList和LinkedList是Java集合框架中经常使用的类.如果你只知道从基本性能比较ArrayList和LinkedList,那么请仔细阅读这篇文章. ArrayList应该在需要更多 ...

  7. java中ArrayList 和 LinkedList 有什么区别

    转: java中ArrayList 和 LinkedList 有什么区别 ArrayList和LinkedList都实现了List接口,有以下的不同点:1.ArrayList是基于索引的数据接口,它的 ...

  8. Java中关于HashMap的元素遍历的顺序问题

    Java中关于HashMap的元素遍历的顺序问题 今天在使用如下的方式遍历HashMap里面的元素时 1 for (Entry<String, String> entry : hashMa ...

  9. JavaScript向select下拉框中加入和删除元素

    JavaScript向select下拉框中加入和删除元素 1.说明 a   利用append()方法向下拉框中加入元素 b   利用remove()方法移除下拉框中最后一个元素 2.设计源代码 < ...

随机推荐

  1. ECharts 从后台动态获取数据 (asp.net)

    (一) 使用工具 visual studio 2017:Web开发:asp.net (代码中的js引用路径以及ajax方法调用的url,记得修改哦) (二) 准备工作(此处写给和我一样小白) 1.动态 ...

  2. 插入排序-C#实现

    插入排序包括:直接插入排序和希尔排序. 具体代码如下: 直接插入排序: /// <summary> /// 直接插入排序 /// 适用于少量元素的排序 /// 稳定性:稳定 /// 时间复 ...

  3. Taro 常用 API

    table th:first-of-type { width: 300px; } Taro 常用 API 说明 网址 Taro.getSystemInfoSync() 获取系统信息同步接口. http ...

  4. S50 抓取pattern数据

    S50(原V50) 测试机台湾久元电子研发的一款数字芯片测试系统,行业内有很多人使用: 现在记录下S50抓取pattern数据的一些方法: 程序主要是通过read_log配合c代码实现,pattern ...

  5. php正则表达式提取img alt/title标签并替换

    有时我们需要对富文本编辑器中的img标签进行必要的处理以满足网站自身的需要,比如:根据站点关键词对页面内img的alt标签设定关键词,以下为提取并替换alt/title标签内容的正则: $title ...

  6. python用字符串调用当前模块内的函数

    eval(字符串)() vars()[字符串]() 例如:

  7. MacOS:Django + Python3 + MySQL

    Django Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站 ...

  8. CentOS 7系统初始化

    1. 升级系统 $ yum -y update 2.SELinux设置: 禁用 $ vi /etc/selinux/config 修改 SELINUX=disabled

  9. SpringCloudConfig配置中心git库以及refresh刷新

    基于git库的Spring Cloud Config配置中心代码demo下载地址:https://gitlab.com/mySpringCloud/config-git SpringBoot版本: 1 ...

  10. HTML+CSS 对于英文单词强制换行但不截断单词的解决办法

    如何处理长的单词和链接(强制换行,连接符,省略号等) 我们在前端开发中经常会遇到一些很长的文本串从它的容器中溢出,例如: 通过这样一段css可以有效解决这种问题: .dont-break-out { ...