今天楼主继续分享一道经典Java面试题并进行相关知识点的拓展:

上题:

写一段代码在遍历 ArrayList 时移除一个元素?
该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法。是使用正确的方式来实现在遍历的过程中移除元素,而不会出现 ConcurrentModificationException 异常的示例代码。

于是对ArrayList移除一个元素的相关知识点进行了拓展,查阅到了这些资料:

今天写了一道题,题目是这样的:

一个ArrayList对象aList中存有若干个字符串元素,现欲遍历该ArrayList对象,删除其中所有值为"abc"的字符串元素,请用代码实现。

很简单,直接上代码:

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. ArrayList<String> aList = new ArrayList<String>();
  4. aList.add("a");
  5. aList.add("ab");
  6. aList.add("abc");
  7. aList.add("abcr");
  8. aList.add("abc");
  9. aList.add("abcf");
  10. aList.add("abc");
  11. aList.add("abdc");
  12. for(int i = 0;i < aList.size();i++){
  13. if(aList.get(i).equals("abc")){
  14. aList.remove(i);
  15. }
  16. }
  17. System.out.println(aList);
  18. }
  19. }

输出结果为:[a, ab, abcr, abcf, abdc]

也可以使用迭代器来遍历:

  1. Iterator<String> iter = aList.iterator();
  2. while(iter.hasNext()){
  3. if(iter.next().equals("abc")){
  4. iter.remove();
  5. }

结果与上面相同。

后来改了数据,多加了一个“abc”:

  1. ArrayList<String> aList = new ArrayList<String>();
  2. aList.add("a");
  3. aList.add("ab");
  4. aList.add("abc");
  5. aList.add("abc");  //多加的一行
  6. aList.add("abcr");
  7. aList.add("abc");
  8. aList.add("abcf");
  9. aList.add("abc");
  10. aList.add("abdc");

然后再用for循环遍历,结果变为:

[a, ab, abc, abcr, abcf, abdc]   发现有一个“abc”没有被移除掉。

然而使用迭代器,答案是对的,所有的“abc”都被移除掉了。

原因:检查后发现。在for循环里,当清除掉前一个“abc”后,索引会指向下一个“abc”,然而还做了i++操作,等于直接将这个“abc”跳了过去去执行后面的步骤,从而使它“逃过法网”。

而迭代器不会有这样的问题是因为hasNext()方法,原理是指针向后移动,每运行一次it.next(),指针向后移动一次,一个一个的遍历。

总结:可以在for循环中做一点小处理,如下:

  1. for(int i = 0;i < aList.size();i++){
  2. if(aList.get(i).equals("abc")){
  3. aList.remove(i);
  4. i--;
  5. }
  6. }

每次清除掉“abc”之后执行i--操作,下一回循环再执行i++操作,就相当于抵消啦。

为了避免此类问题的出现,尽量还是用迭代器比较好。

还有一种办法就是:我们知道ArrayList的底层是用数组实现的,如果你删除了其中一个元素,那么后边的元素都会向前移动。所以在遍历时如果删除元素,就要小心了。用数组下标进行遍历,如果需要删除元素,我们从后向前遍历,这样不论有没有元素删除,我们都不会遗漏未被遍历的元素。

这是ArrayList遍历的时候删除某元素的两种办法,我们还是尽量用迭代器更加的好

下面将针对java.util.ArrayList在foreach循环遍历时删除元素的问题

也就是此面试题中所说的ConcurrentModificationException 异常的问题(可能foreach循环只是出现这个异常的一种原因)

查阅了相关的资料,现在分享给大家

ArrayList是java开发时非常常用的类,常碰到需要对ArrayList循环删除元素的情况。这时候大家都不会使用foreach循环的方式来遍历List,因为它会抛java.util.ConcurrentModificationException异常。比如下面的代码就会抛这个异常:

  1. <span style="white-space:pre">    </span>List list = new ArrayList();
  2. list.add("1");
  3. list.add("2");
  4. list.add("3");
  5. list.add("4");
  6. list.add("5");
  7. for (String item : list) {
  8. if (item.equals("3")) {
  9. System.out.println(item);
  10. list.remove(item);
  11. }
  12. }
  13. System.out.println(list.size());

那是不是在foreach循环时删除元素一定会抛这个异常呢?答案是否定的。

见这个代码:

Listlist=newArrayList();

list.add("1");

list.add("2");

list.add("3");

list.add("4");

list.add("5");

for(Stringitem:list){

if(item.equals("4")){

System.out.println(item);

list.remove(item);

}

}

System.out.println(list.size());

这段代码和上面的代码只是把要删除的元素的索引换成了4,这个代码就不会抛异常。为什么呢?

接下来先就这个代码做几个实验,把要删除的元素的索引号依次从1到5都试一遍,发现,除了删除4之外,删除其他元素都会抛异常。接着把list的元素个数增加到7试试,这时候可以发现规律是,只有删除倒数第二个元素的时候不会抛出异常,删除其他元素都会抛出异常。

好吧,规律知道了,可以从代码的角度来揭开谜底了。

首先java的foreach循环其实就是根据list对象创建一个Iterator迭代对象,用这个迭代对象来遍历list,相当于list对象中元素的遍历托管给了Iterator,你如果要对list进行增删操作,都必须经过Iterator,否则Iterator遍历时会乱,所以直接对list进行删除时,Iterator会抛出ConcurrentModificationException异常

其实,每次foreach迭代的时候都有两部操作:

  1. iterator.hasNext()  //判断是否有下个元素
  2. item = iterator.next()  //下个元素是什么,并赋值给上面例子中的item变量

hasNext()方法的代码如下:

  1. public E next() {
  2. checkForComodification();
  3. try {
  4. E next = get(cursor);
  5. lastRet = cursor++;
  6. return next;
  7. } catch (IndexOutOfBoundsException e) {
  8. checkForComodification();
  9. throw new NoSuchElementException();
  10. }
  11. }
  12. final void checkForComodification() {
  13. if (modCount != expectedModCount)
  14. throw new ConcurrentModificationException();
  15. }
  16. }

这时候你会发现这个异常是在next方法的checkForComodification中抛出的,抛出原因是modCount != expectedModCount

  • modCount是指这个list对象从new出来到现在被修改次数,当调用List的add或者remove方法的时候,这个modCount都会自动增减;
  • expectedModCount是指Iterator现在期望这个list被修改的次数是多少次。

iterator创建的时候modCount被赋值给了expectedModCount,但是调用list的add和remove方法的时候不会同时自动增减expectedModCount,这样就导致两个count不相等,从而抛出异常。

如果想让其不抛出异常,一个办法是让iterator在调用hasNext()方法的时候返回false,这样就不会进到next()方法里了。这里cursor是指当前遍历时下一个元素的索引号。比如删除倒数第二个元素的时候,cursor指向最后一个元素的,而此时删掉了倒数第二个元素后,cursor和size()正好相等了,所以hasNext()返回false,遍历结束,这样就成功的删除了倒数第二个元素了。

破除迷信,foreach循环遍历的时候不能删除元素不是绝对,倒数第二个元素是可以安全删除的~~(当然以上的思路都是建立在list没有被多线程共享的情况下)

资料转载于:http://blog.csdn.net/zhuhai__yizhi/article/details/49992321

http://blog.csdn.net/u011665766/article/details/50697580

http://blog.csdn.net/hongchangfirst/article/details/49780389

写一段代码在遍历 ArrayList 时移除一个元素?相关推荐

  1. 遍历ArrayList时移除重复元素失效问题

    遍历ArrayList时移除重复元素失效问题 在使用ArrayList时遇到个问题,例如: public static void remove(ArrayList<String> list ...

  2. 遍历ArrayList并移除一个元素

    前言 这是一个比较经典的面试题,相信也会有不少人遇到,今天就在此记录一下,写了两种方式供大家参考. 方式一 在for循环中删除元素,倒序遍历ArrayList能够有效防止漏删,这里大家可能会有疑问了? ...

  3. 当程序员说“这代码写的可真烂”,他们的意思是“这烂代码不是我写的”。而当他们说这段代码有些“小问题”时,很可能这代码是他们自己写的...

    英文原文:What Programmers Say vs. What They Mean 你是否听到过同事说"这段代码不言自明"?你的同事的这句话的实际意思是这段代码不需要写注释. ...

  4. (63)FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(二)

    1.1 FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(二) 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-用verilo ...

  5. (62)FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(一)

    1.1 FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(一) 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-用verilo ...

  6. 用 wait-notify 写一段代码来解决生产者-消费者问题

    用 wait-notify 写一段代码来解决生产者-消费者问题 参考文章: (1)用 wait-notify 写一段代码来解决生产者-消费者问题 (2)https://www.cnblogs.com/ ...

  7. 在记事本上用java写一段代码,程序到底是如何跑起来的?

    首先我们在记事本上写一段代码,并把类型改成java文件 不知道大家有没有想过,当我们写下这样一段代码,计算机是如何来运行它的? 它的运行路径如下: 第一步:我们在记事本上写代码,它是一个.java文件 ...

  8. 不写一段代码来获取扇贝单词的接口数据

    不写一段代码来获取扇贝单词的接口数据 第一步,登录并寻找可以爬取的数据(想直接看结论可以到第四步) 第二步,对症下药 第三步,解密接口 第四步,使用现成的方法获取数据 最近想做一个背单词相关的app, ...

  9. 写一段代码将a_b_ _c_ _ _d_转换成_ _ _ _ _ _ _abcd

    写一段代码将a_b_ _c_ _ _d_转换成_ _ _ _ _ _ _abcd; int main(int argc, const char * argv[]) {char str[20];int ...

最新文章

  1. 科大星云诗社动态20220114
  2. C++中operator的两种用法
  3. MobaXterm中修改服务器ip,如何使用mobaxterm登录云服务器
  4. qmenu点击后不关闭_速腾关闭点火开关后发动机不立即熄火
  5. sap后台配置原因代码_【MM配置】Inventory Management 库存管理
  6. python必背代码-Python一些实用代码
  7. 微信小程序-获取当前城市位置经纬度,并解析位置信息
  8. 【致远FAQ】A6+Cloud__V1.0_A6+cloud的M3端地址保存提示:not found
  9. Python爬虫理论 | (4) 数据存储
  10. tensorflow2没有slim模块
  11. java 自然对数的底数_Java求自然对数底e的值
  12. 利用snowfall.jquery.js实现爱心满屏飞或点点满屏飞
  13. 硕士毕业论文写多少字
  14. Java程序设计基础【3】
  15. 一期每日一GO群分享-flag、viper、协程池、异常处理
  16. 正益工作能担起PaaS+SaaS的未来探索吗?
  17. Java正则表达式校验邮箱和手机号
  18. 中国汉字书法的回溯感和信息熵
  19. C++11多线程第一篇:并发基本概念及实现,进程、线程基本概念
  20. 松饼行业调研报告 - 市场现状分析与发展前景预测

热门文章

  1. 面向对象基础实战之英雄联盟
  2. 添加arcgi移动地图点位事件
  3. exe 文件打包和 cmd 常用命令
  4. infuse播放4k视频卡顿
  5. 股权常识:32套学习股权设计方案
  6. CSS background 属性全家桶
  7. 手机照片随心打 佳能召开打印新品体验会
  8. 小酒坊酿白酒不知道怎么售卖?质检报告和SC生产许可不知道怎么办理?
  9. 抖音检测报告怎么办理?测试周期要多长时间?
  10. 希尔伯特第 13 问题,Kolmogorov–Arnold representation theorem 和通用近似定理(Universal approximation theorem)