范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

忏悔篇移除List中的元素引发的异常

  遍历集合出现的问题
  之前遇到对List进行遍历删除的时候,出现来一个 ConcurrentModificationException   异常,可能好多人都知道list遍历不能直接进行删除操作,但是你可能只是跟我一样知道结果,但是不知道为什么不能删除,或者说这个报错是如何产生的,那么我们今天就来研究一下。一、异常代码
  我们先看下这段代码,你有没有写过类似的代码 public static void main(String[] args) {    List list = new ArrayList<>();    System.out.println("开始添加元素 size:" + list.size());    for (int i = 0; i < 100; i++) {     list.add(i + 1);   }    System.out.println("元素添加结束 size:" + list.size());    Iterator iterator = list.iterator();    while (iterator.hasNext()) {     Integer next = iterator.next();     if (next % 5 == 0) {       list.remove(next);     }   }   System.out.println("执行结束 size:" + list.size()); }
  「毫无疑问,执行这段代码之后,必然报错,我们看下报错信息。」
  我们可以通过错误信息可以看到,具体的错误是在 checkForComodification   这个方法产生的。二、ArrayList源码分析
  首先我们看下 ArrayList  的iterator  这个方法,通过源码可以发现,其实这个返回的是ArrayList  内部类的一个实例对象。public Iterator iterator() {   return new Itr(); }
  我们看下 Itr  类的全部实现。private class Itr implements Iterator {   int cursor;       // index of next element to return   int lastRet = -1; // index of last element returned; -1 if no such   int expectedModCount = modCount;    Itr() {}    public boolean hasNext() {     return cursor != size;   }    @SuppressWarnings("unchecked")   public E next() {     checkForComodification();     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];   }    public void remove() {     if (lastRet < 0)       throw new IllegalStateException();     checkForComodification();      try {       ArrayList.this.remove(lastRet);       cursor = lastRet;       lastRet = -1;       expectedModCount = modCount;     } catch (IndexOutOfBoundsException ex) {       throw new ConcurrentModificationException();     }   }    @Override   @SuppressWarnings("unchecked")   public void forEachRemaining(Consumer<? super E> consumer) {     Objects.requireNonNull(consumer);     final int size = ArrayList.this.size;     int i = cursor;     if (i >= size) {       return;     }     final Object[] elementData = ArrayList.this.elementData;     if (i >= elementData.length) {       throw new ConcurrentModificationException();     }     while (i != size && modCount == expectedModCount) {       consumer.accept((E) elementData[i++]);     }     // update once at end of iteration to reduce heap write traffic     cursor = i;     lastRet = i - 1;     checkForComodification();   }    final void checkForComodification() {     if (modCount != expectedModCount)       throw new ConcurrentModificationException();   } }
  「参数说明:」
  cursor   : 下一次访问的索引;
  lastRet   :上一次访问的索引;
  expectedModCount   :对ArrayList修改次数的期望值,初始值为modCount  ;
  modCount   : 它是AbstractList  的一个成员变量,表示ArrayList  的修改次数,通过add  和remove  方法可以看出;
  「几个常用方法:」
  hasNext()  :public boolean hasNext() {  return cursor != size; }
  如果下一个访问元素的下标不等于 size  ,那么就表示还有元素可以访问,如果下一个访问的元素下标等于size  ,那么表示后面已经没有可供访问的元素。因为最后一个元素的下标是size()-1  ,所以当访问下标等于size  的时候必定没有元素可供访问。
  next()  :public E next() {   checkForComodification();   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]; }
  注意下,这里面有两个非常重要的地方, cursor  初始值是0,获取到元素之后,cursor   加1,那么它就是下次索要访问的下标,最后一行,将i  赋值给了lastRet  这个其实就是上次访问的下标。
  此时, cursor  变为了1,lastRet  变为了0。
  最后我们看下 ArrayList  的remove()  方法做了什么?public boolean remove(Object o) {   if (o == null) {     for (int index = 0; index < size; index++)       if (elementData[index] == null) {         fastRemove(index);         return true;       }   } else {     for (int index = 0; index < size; index++)       if (o.equals(elementData[index])) {         fastRemove(index);         return true;       }   }   return false; } private void fastRemove(int index) {   modCount++;   int numMoved = size - index - 1;   if (numMoved > 0)     System.arraycopy(elementData, index+1, elementData, index,                      numMoved);   elementData[--size] = null; // clear to let GC do its work }
  「重点:」
  我们先记住这里, modCount  初始值是0,删除一个元素之后,modCount  自增1,接下来就是删除元素,最后一行将引用置为null  是为了方便垃圾回收器进行回收。三、问题定位
  到这里,其实一个完整的判断、获取、删除已经走完了,此时我们回忆下各个变量的值:
  cursor   : 1(获取了一次元素,默认值0自增了1);
  lastRet   :0(上一个访问元素的下标值);
  expectedModCount   :0(初始默认值);
  modCount   : 1(进行了一次remove  操作,变成了1);
  不知道你还记不记得, next()  方法中有两次检查,如果已经忘记的话,建议你往上翻一翻,我们来看下这个判断:final void checkForComodification() {   if (modCount != expectedModCount)     throw new ConcurrentModificationException(); }
  当 modCount  不等于expectedModCount  的时候抛出异常,那么现在我们可以通过上面各变量的值发现,两个变量的值到底是多少,并且知道它们是怎么演变过来的。那么现在我们是不是清楚了ConcurrentModificationException  异常产生的愿意呢!
  「就是因为, list.remove()  导致modCount  与expectedModCount  的值不一致从而引发的问题。」四、解决问题
  我们现在知道引发这个问题,是因为两个变量的值不一致所导致的,那么有没有什么办法可以解决这个问题呢!答案肯定是有的,通过源码可以发现, Iterator  里面也提供了remove  方法。public void remove() {   if (lastRet < 0)     throw new IllegalStateException();   checkForComodification();    try {     ArrayList.this.remove(lastRet);     cursor = lastRet;     lastRet = -1;     expectedModCount = modCount;   } catch (IndexOutOfBoundsException ex) {     throw new ConcurrentModificationException();   } }
  你看它做了什么,它将 modCount  的值赋值给了expectedModCount  ,那么在调用next()  进行检查判断的时候势必不会出现问题。
  那么以后如果需要 remove  的话,千万不要使用list.remove()  了,而是使用iterator.remove()  ,这样其实就不会出现异常了。public static void main(String[] args) {    List list = new ArrayList<>();    System.out.println("开始添加元素 size:" + list.size());    for (int i = 0; i < 100; i++) {     list.add(i + 1);   }    System.out.println("元素添加结束 size:" + list.size());    Iterator iterator = list.iterator();    while (iterator.hasNext()) {     Integer next = iterator.next();     if (next % 5 == 0) {       iterator.remove();     }   }   System.out.println("执行结束 size:" + list.size()); }
  「建议:」
  另外告诉大家,我们在进行测试的时候,如果找不到某个类的实现类,因为有时候一个类有超级多的实现类,但是你不知道它到底调用的是哪个,那么你就通过 debug  的方式进行查找,是很便捷的方法。五、总结
  其实这个问题很常见,也是很简单,但是我们做技术的就是把握细节,通过追溯它的具体实现,发现它的问题所在,这样你不仅仅知道这样有问题,而且你还知道这个问题具体是如何产生的,那么今后不论对于你平时的工作还是面试都是莫大的帮助。

姐,这3000是我的压岁钱,你拿去学校花澎湃新闻记者姚似璐姐姐,这是我的压岁钱,你拿到学校去花!想吃什么就去买!新学期报到前,杭州20岁的大三女生郑珂晶在行李箱夹层发现3000元现金,还有一张读小学六年级的妹妹写的纸条。安徽新锐集团丨滁州儒林外国语学校儒林学子如何实现人手一墩2022年北京冬奥会上除了赛场上的运动员们引人瞩目之外,作为吉祥物的冰墩墩也受到了全国人民的欢迎。在各大售卖平台上,一度出现了一墩难求的情况。如今儒林学校七年级的孩子们却靠自己努力加大金融支持力度,围绕重点领域和群体,提出多项千亿贷款增长计划让市场主体金融机构强信心增元气政策要点安排不少于1000亿元再贷款资金,设立支小惠商贷助农振兴贷等财金互动产品,财政按照1。5给予贴息,贴息后借款利率不超过一年期贷款基础利率(LPR)加15个基点(BP)开展民近1500家瑞士旅游产业机构参与可持续旅游项目2月6日起,全国旅行社及在线旅游企业经营中国公民赴有关国家出境团队旅游和机票酒店业务试点恢复,瑞士是20个试点国家之一。据了解,凯撒旅游2023年首个出境商务考察团将于2月7日从杭投资238亿元!福特计划与宁德时代合作,建立电动汽车电池厂视频加载中(央视财经正点财经)当地时间13日,美国汽车制造商福特公司宣布,计划与中国动力电池制造商宁德时代合作,投资35亿美元,约合人民币238亿元,在密歇根州建立一家电动汽车电池广元青溪镇以文塑旅以旅彰文打造乡村文化振兴省级样板来源四川融媒体省平台位于四川省广元市青川县西部边缘摩天岭脚下的青溪镇,交通便利,民风淳厚,文化积淀深厚,旅游产业方兴未艾。近年来,该镇坚持以文塑旅以旅彰文,推进文化和旅游深度融合发古人为何热衷大量陪葬?从先秦两汉时期的赙赠之礼,看古人丧葬观阅读此文前,麻烦您点击一下关注,既方便您进行讨论与分享,又给您带来不一样的参与感,感谢您的支持。引言我国古代人民常说逝者为大,这句话也显示了对去世之人的尊重与敬仰。但是早期的人们并消失于互联网时代的10件事如今,我们几乎很难想象一个没有互联网的世界是什么模样。新冠疫情期间,线上生活一度不只是一种可选的生活方式,而成了我们赖以生存的世界本身。甚至在疫情后迎来的重启,都是互联网时代语言的黄河上游出现大面积ampampquot流冰花ampampquot央广网西宁2月14日消息(记者贾海元)近日,随着气温回升,位于青海省果洛藏族自治州久治县境内的黄河河面结冰开始融化,融化后的冰块随着河水流动,形成奇特的流冰花景观。流冰花在两岸巍峨探寻陕西省西安市革命公园这是西安革命公园正门革命公园有十几年都没有去过了,原来孩子小也经常带孩子去玩,一般带孩子去玩都是只在儿童游乐场玩。里面没有好好转过。今天下午在家没事就去了革命公园里面转了一圈,回来蜡梅花期接近尾声,普陀真如公园内依旧暗香浮动蜡梅清香四溢,为冬季观赏佳品,如今立春已过,蜡梅的花期接近了尾声,而普陀区真如公园内的蜡梅依然盛开着,公园内暗香浮动,鹅黄色的蜡梅绽放枝头,如果你对蜡梅的清香还依依不舍,那一定要来
世界十大拥堵城市,第一名的城市连红绿灯都没有大城市往往免不了拥堵的情况,随着搬迁至市中心的人越来越多,一些大城市的扩建速度远远赶不上当地的人口膨胀。今天,我们为你带来了全球最拥堵城市。你去过的最拥堵的地方是哪里呢?第十名雅加回望近代剌杀事件,结果令人唏嘘(下)话中有画202207110015昨天因为安倍晋三被刺身亡,写了这个题目的文章,列举了近代的刺杀林肯伊藤博文载沣肯尼迪岸信介朴正熙事件,受到网友的好评。但也有朋友留言,说历史上还有一唐山女教授将被依法依规严肃处理7月8日安倍晋三演讲中遇刺,立马成为当天各大媒体头条,各个微信群,朋友圈都在讨论,而唐山女教授石某瑛迅速抢占了舆论头条。一大家先来认识一下这位女教授石某瑛今年60岁,唐山师范学院退日媒自民党参院议席数预计超过改选前参考消息网7月10日报道据共同社7月10日报道,共同社的出口调查结果显示,自民党此次参院选举获得的议席数超过改选前(55个议席)基本成定局。此外,出口调查结果还显示,对修改宪法态度杨丞琳连续8年为丈夫庆生,晒牵手照甜蜜告白,李荣浩回应送飞吻7月11日是李荣浩的生日,正式迈入37岁的崭新人生。他发文表示有家人朋友和歌迷的陪伴,人生很满足。如此重要的日子,自然少不了妻子的祝福。杨丞琳早在零点便准时分享了二人的合影,并甜蜜天道小摊贩收两次饭钱,丁元英为什么不与其辩解没想到,十几年前的一部剧天道,在2020年再次火爆全网。于是,我刷完电视剧后,立马又找来了原著小说遥远的救世主,趁热打铁,细细品读了一番。初次读原著小说,就被其文字内容深深吸引。文佛系女王坛蜜另类婚姻如鱼得水,至情至性般体验各种人生滋味坛蜜,因出演数部大热限制级电影而出名。她的眼睛总是微微眯着,像是浑不在意自己的冶艳令无数人着迷。28岁时,她以本名斋藤支静加参加选秀,出道后给自己取艺名坛蜜,这个名字来源于佛教,坛葡萄牙与明朝的战争,第一次让欧洲殖民者遵守规矩,老实了几百年公元1514年,大明正德九年,这一年被视作全球化的开端,也正是一年,一个葡萄牙人远渡重洋,从广州登陆,希望和明朝展开贸易,这是自马可波罗之后有记载的第一个来到中国的欧洲人,他不知道安倍出事第二天!矢野浩二发跳舞视频,评论区被疯狂调侃日本前首相安倍晋三遇刺事件,成为全世界网友的热议话题。中国网友的评论咱就不予置评了。而在中国娱乐圈发展的日本演员矢野浩二,却备受网友关注。矢野浩二发跳舞视频被调侃!显而易见,大家就Summit迎战Ale!FPX迎战LNG,再赢一场就去西部北京时间2022年7月11日,英雄联盟LPL夏季赛第六周第一日即将分别迎来FPX对阵LNGJDG对阵RA这两场比赛。第一场FPXVSLNGFPX首发SummitClidCareLW江苏省军区新招录文职人员入列,请检阅文图祝心润谢宇晖青春无问西东,岁月自成芳华。近日,江苏省军区40余名新招录文职人员集聚空军勤务学院进行岗前培训。新招录文职人员利用周五党团活动时间进行宣誓活动。实现从地方青年到军队