千锋教育-做有情怀、有良心、有品质的职业教育机构

400-811-9990
手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

上海
  • 北京
  • 郑州
  • 武汉
  • 成都
  • 西安
  • 沈阳
  • 广州
  • 南京
  • 深圳
  • 大连
  • 青岛
  • 杭州
  • 重庆
当前位置:沈阳千锋IT培训  >  技术干货  >  ConcurrentModificationException异常的解决

ConcurrentModificationException异常的解决

来源:千锋教育
发布人:lx
时间: 2023-03-30 10:29:07

  一. 前言

  最近一个学生,在使用使用ArrayList的subList的时候,发生了ConcurrentModificationException的异常。老师觉得这个现象非常具有代表性,估计有不少同学都会在同样的问题上犯迷糊,所以今天特意把这个问题记录下来,供大家参考。

ConcurrentModificationException异常

  二. 异常场景

  1. 代码如下

  下面是产生异常的代码。

  public static void main(String[] args) throws InterruptedException {

  // 1 定义一个String的集合,并且添加3个元素 【"z","k","x"】

  List sourceList = new ArrayList(2);

  sourceList.add("z");

  sourceList.add("k");

  sourceList.add("x");

  // 2 使用subList方法 表面上把集合的前2个元素截取出来放在新的集合newList中

  // 表面上此时newList 应该是【"z","k"】

  List newList = sourceList.subList(0, 2);

  // 3 往sourceList的0号位置 添加新的元素 sourceList应该变成了 【“cs”,"z","k",“x”】

  sourceList.add(0,"cs");

  // 4 意图在控制台上打印新的集合newList的0号位置 想观察一次此时newList的0号元素到底是什么

  // 结果如果是 "z" 说明:【newList 是 sourceList的副本,此时两者已经没什么关系】

  // 结果如果是 "cs" 说明:【newList 是 sourceList的视图,修改sourceList会影响到newList】

  // 结果意想不到的是 这个地方抛出了ConcurrentModificationException 异常

  System.out.println(newList.get(0));

  }

  2. 异常截图

7a0ff49c-d3a8-4a03-befc-fc28d3c48566

  三. 原因分析

  3.1 modCount变量

  ArrayList 中定义了一个变量modCount,顾名思义这个变量就是用来记录ArrayList被修改的次数,modCount 初始值为0,ArrayList 源代码如下。

  protected transient int modCount = 0;

  3.2 modCount解析

  ArrayList 中每每有增删改的变动,都会导致modCount加1,源代码如下。

  // 例如 ArrayList 的 add 方法

  public boolean add(E e) {

  ensureCapacityInternal(size + 1); // Increments modCount!! 在这里modCount+1

  elementData[size++] = e;

  return true;

  }

  // 继续深入到 ensureCapacityInternal 方法

  private void ensureCapacityInternal(int minCapacity) {

  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

  minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

  }

  ensureExplicitCapacity(minCapacity); // 在这里modCount+1 这里再继续深入

  }

  // 继续深入到ensureExplicitCapacity

  private void ensureExplicitCapacity(int minCapacity) {

  modCount++;

  // overflow-conscious code

  if (minCapacity - elementData.length > 0)

  grow(minCapacity);

  }

  3.3 SubList源码

  ArrayList 使用SubList的时候并没有创建新的List,而是引用原来的List,并且把原本List的modCount复制了过来,源代码如下。

85ada4b2-a904-479b-84dd-d676c0dd99a9

  3.4 ConcurrentModificationException异常产生原因

  subList在做增伤改查时,都会对比一下自己的modCount 和 原生的list的modCount,如果对应不上就会抛出ConcurrentModificationException异常,源代码如下。

  // subList的get 方法 调用之前要checkForComodification

  public E get(int index) {

  rangeCheck(index);

  checkForComodification();

  return ArrayList.this.elementData(offset + index);

  }

  // 跟入到checkForComodification

  private void checkForComodification() {

  // 比较subList 的modCount 和 源list的modCount 不相等等则抛出异常

  if (ArrayList.this.modCount != this.modCount)

  throw new ConcurrentModificationException();

  }

  四. 结论和解决方案

  4.1 原因总结

  至此,我们在上面代码中发生异常的原因已经一目了然了,再来看一下源代码:

426f87da-2994-43b2-9943-0121b114e55b

  4.2 解决方案

  为了避免出现ConcurrentModificationException异常,我们在开发时要慎用subList,可以自行使用stream来截取需要的部分。

  // List newList = sourceList.subList(0, 2);

  // 使用流的方式代替subList截取

  List newList = sourceList.stream().skip(0).limit(2).collect(Collectors.toList());

  可以对subList 进行二次封装,封装成一个新的ArrayList。

  List newList = sourceList.subList(0, 2);

  // 对subList的结果,再次封装成新的List

  ArrayList newList1 = new ArrayList<>(newList);

  五. 后话

  其实上面的这个异常问题,在阿里巴巴的开发规范中早有说明,原文如下:

  【强制】在 subList 场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、 增加、删除产生 ConcurrentModificationException 异常。

声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。

猜你喜欢LIKE

为什么会出现ref?

2023-05-05

防抖和节流是什么?

2023-04-27

JS的原型与原型链是什么?

2023-04-20

最新文章NEW

什么是redux?

2023-05-05

vue的计算属性,特性,应用是什么?

2023-04-27

负载均衡原理是什么?

2023-04-25

相关推荐HOT

更多>>

快速通道 更多>>

最新开班信息 更多>>

网友热搜 更多>>