集合在迭代过程中能否添加、删除或修改元素
来源:互联网 发布:淘宝宝贝所在地设置 编辑:程序博客网 时间:2024/05/16 13:07
1.使用 for 循环
List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); for (int i = 0; i < list.size(); i++) { System.out.println(list.size()); if ("1".equals(list.get(i))){ list.add("4"); list.remove("1"); } }
2.使用 foreach 遍历
List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); for (String s : list){ if ("1".equals(s)){ list.add("4"); list.remove("1"); } }
3.使用 Iterator 迭代器
List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { if ("1".equals(iterator.next())) { iterator.remove(); list.add("4"); list.remove("1"); } }
在第一种情况下编译和运行都是可以的,第二种和第三种则会抛出 java.util.ConcurrentModificationException 的异常,这是为什么呢?
下面一段是来自百度知道的解释:
逻辑上讲,迭代时可以添加元素,但是一旦开放这个功能,很有可能造成很多意想不到的情况。
比如你在迭代一个 ArrayList,迭代器的工作方式是依次返回给你第0个元素,第1个元素,等等,假设当你迭代到第5个元素的时候,你突然在ArrayList的头部插入了一个元素,使得你所有的元素都往后移动,于是你当前访问的第5个元素就会被重复访问。
java 认为在迭代过程中,容器应当保持不变。因此,java 容器中通常保留了一个域称为 modCount,每次你对容器修改,这个值就会加1。当你调用 iterator 方法时,返回的迭代器会记住当前的 modCount,随后迭代过程中会检查这个值,一旦发现这个值发生变化,就说明你对容器做了修改,就会抛异常。
我们先看第三种情况,即使用 Iterator 迭代器对集合进行遍历,我们以 AbstractList 为例。
首先来看一下AbstractList是如何创建Iterator的,AbstractList有一个内部类:
private class Itr implements Iterator<E> { ... }
而创建Iterator需要调用iterator()方法:
public Iterator<E> iterator() { return new Itr(); }
所以在调用集合的iterator方法之后实际上返回了一个内部类的实例。
我们看一下Itr这个类的next()方法是如何实现的:
public E next() { checkForComodification(); try { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } }
checkForComodification()方法的实现如下:
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
modCount表示集合的元素被修改的次数,每次增加或删除一个元素的时候,modCount都会加一,而expectedModCount用于记录在集合遍历之前的modCount,检查这两者是否相等就是为了检查集合在迭代遍历的过程中有没有被修改,如果被修改了,就会在运行时抛出ConcurrentModificationException这个RuntimeException,以提醒开发者集合已经被修改。
这就说明了为什么集合在使用Iterator进行遍历的时候不能使用集合本身的add或者remove方法来增减元素。但是使用Iterator的remove方法是可以的,至于原因可以看一下这个方法的实现:
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } }
可以看到,在集合的元素被remove之后,expectedModCount被重新赋值,是的modCount总是等于expectedModCount,所以不会抛出ConcurrentModificationException异常。
而上面提到的第二种使用foreach来对集合进行遍历本质上和第三种情况是一样的,因为根据Oracle提供的文档,foreach内部的实现机制其实就是使用的Iterator。
第一种使用for循环进行遍历时内部使用的就是集合本身的遍历方法,这里不做讨论。
- 集合在迭代过程中能否添加、删除或修改元素
- 集合迭代删除元素
- 在list集合中的添加、修改、删除和遍历元素
- STL在迭代的过程中,删除指定的元素
- STL容器迭代过程中删除元素技巧(转)
- STL容器迭代过程中删除元素技巧(转)
- STL容器迭代过程中删除元素技巧(转)
- ArrayList集合中 迭代 加元素
- 添加或删除元素
- select中添加、修改、删除option元素
- select中添加、修改、删除option元素
- select中添加、修改、删除option元素
- 为什么java中迭代过程中不可以不可以添加或删除元素
- Java中List迭代过程中删除、新增元素的处理
- 删除正在循环迭代的集合元素的分析
- 在循环中删除集合的元素
- 集合遍历过程中删除集合元素问题
- 如何实现在遍历集合的过程中删除其中的元素
- Eclipse XML报错 cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be fou
- SQL的四种连接-左外连接、右外连接、内连接、全连接
- Java中ConcurrentHashMap学习
- 最新100道java面试题
- pytjon基础
- 集合在迭代过程中能否添加、删除或修改元素
- JQ学习之层次选择器的几个样例
- docker 初步体验
- TinkPHP框架总结一
- php 模式(适配器模式)
- 揭开Java中关键字final的面纱
- bzoj 1069: [SCOI2007]最大土地面积(旋转卡壳)
- linux和window 双系统文件修改
- python 基础脚本练习