Java集合之ConcurrentModificationException(并发修改异常)分析
来源:互联网 发布:mac免费游戏推荐 编辑:程序博客网 时间:2024/06/05 01:59
前言
今天写LeetCode遇到一道题,我想利用作为方法参数的一个集合作为返回的值,来达到节省空间的目的:
public List<Interval> merge(List<Interval> intervals) {}
意思就是,我想对集合intervals进行修改,然后返回值就传修改后的intervals。
然后顺势就联想到了并发修改异常,之前只是知道个概念,并没有仔细思考过。今天就来分析一下ConcurrentModificationException。
ConcurrentModificationException
ConcurrentModificationException是开发中一个常见的异常,多发生于对一个Collection进行边遍历边做影响size变化的操作时,比如说遍历集合同时向集合中添加新的元素。
举例
下面我们进行三种操作:
1.利用for循环遍历集合的同时添加元素
List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); for (int i = 0; i < list.size(); i++) { if (list.get(i) == 2) { list.add(10); } }
没有问题。
2.利用迭代器遍历集合的同时添加元素
List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { if (iterator.next() == 2) { list.add(10); } }
出现了问题,编译器提示:Exception in thread “main” java.util.ConcurrentModificationException。
3.利用超级for循环遍历集合的同时添加元素
List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); for (int num : list) { if (num == 2) { list.add(10); } }
同样出现了问题,编译器提示:Exception in thread “main” java.util.ConcurrentModificationException。
分析
以上使用的是ArrayList,那么我们来看一下ArrayList中关于迭代器的源码(Android SDK 23中的Open JDK源码):
@Override public Iterator<E> iterator() { return new ArrayListIterator(); } private class ArrayListIterator implements Iterator<E> { …… /** The expected modCount value */ private int expectedModCount = modCount; @SuppressWarnings("unchecked") public E next() { ArrayList<E> ourList = ArrayList.this; int rem = remaining; if (ourList.modCount != expectedModCount) { throw new ConcurrentModificationException(); } if (rem == 0) { throw new NoSuchElementException(); } remaining = rem - 1; return (E) ourList.array[removalIndex = ourList.size - rem]; } …… }
调用iterator()会返回一个ArrayListIterator对象,ArrayListIterator初始化的时候,会将外部类ArrayList(其实是ArrayList的父类AbstractList中的)的成员变量modCount的值赋给expectedModCount。
然而我们向集合中添加元素的时候,改变了modCount的值:
@Override public boolean add(E object) { Object[] a = array; int s = size; if (s == a.length) { Object[] newArray = new Object[s + (s < (MIN_CAPACITY_INCREMENT / 2) ? MIN_CAPACITY_INCREMENT : s >> 1)]; System.arraycopy(a, 0, newArray, 0, s); array = a = newArray; } a[s] = object; size = s + 1; modCount++; return true; }
而expectedModCount的值没有变,这时候再调用next(),会走进如下判断分支:
if (ourList.modCount != expectedModCount) { throw new ConcurrentModificationException(); }
抛出ConcurrentModificationException。
至于超级for循环遍历呢?其实for-each是个语法糖,编译器会把它转化成迭代器遍历,所以同样会出错。
iterator()不行,我们其实还可以使用listIterator(),它是ArrayList的父类AbstractList中的方法,它返回的是一个FullListIterator对象。我们增删元素就利用FullListIterator的remove()和add(),如下:
List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); ListIterator<Integer> listIterator = list.listIterator(); while (listIterator.hasNext()) { if (listIterator.next().equals(2)) { listIterator.add(10); } }
不会出现问题。
首先它的remove()和add()中调用的就是AbstractList的remove()和add()。
其次它会将expectedModCount的值与modCount的值进行同步,具体可以去查看源码,这里就不做分析了。
参考:Java并发修改错误ConcurrentModificationException分析
- Java集合之ConcurrentModificationException(并发修改异常)分析
- 深入分析集合并发修改异常(源码分析)java.util.ConcurrentModificationException
- Java并发修改异常ConcurrentModificationException
- JAVA ConcurrentModificationException并发修改异常
- ConcurrentModificationException并发修改异常
- ConcurrentModificationException并发修改异常
- java.util.ConcurrentModificationException:并发修改异常!
- java.util.ConcurrentModificationException 并发修改异常处理
- java.util.ConcurrentModificationException ,遍历集合并同时修改集合,并发造成的异常解决办法
- Java并发修改错误ConcurrentModificationException分析
- Java并发修改错误ConcurrentModificationException分析
- Java并发修改错误ConcurrentModificationException分析
- Java并发修改错误ConcurrentModificationException分析
- java语言基础(66)——集合框架(arrayList ConcurrentModificationException 并发修改异常的解决方案)
- 关于迭代器的在集合遍历中发生的并发修改异常(java.util.ConcurrentModificationException
- 黑马程序员——集合中并发修改异常ConcurrentModificationException
- 【Java源码分析】ConcurrentModificationException并发修改异常分析与解决方案(快速失败与安全失败)
- 迭代器并发修改异常ConcurrentModificationException
- JAVA学习图谱
- Java(5-1)
- ReentrantReadWriteLock可重入读写锁
- 分析 ddp wdma 模块
- 前端_添加批量删除查找2
- Java集合之ConcurrentModificationException(并发修改异常)分析
- Lua的系统学习(杂)_使用uLua在Unity中创建一个Sphere小球
- 列表和元组常用操作【python】
- (4.6.27)Android启动优化之打造秒开应用
- PC端google chrome真正实现地理位置模拟,HTML5
- Kubernetes GarbageCollector
- sizeThatFits 和 sizeToFit的区别
- Activity生命周期及其是否可见
- Linux环境下numpy,scipy,matplotlib的安装