JAVA从入门到精通-怎么在遍历的时候删除List元素
金延鑫 2018-04-04 来源 : 阅读 2048 评论 0

摘要:要使JAVA从入门到精通,常见的遍历List的三种方法:使用普通for循环遍历,使用增强型for循环遍历,使用iterator遍历,我们掌握这些技能以后才能JAVA从入门到精通的道路上走的更远。

常见的遍历List的三种方法

· 使用普通for循环遍历

· 使用增强型for循环遍历

· 使用iterator遍历

对于线程不安全的ArrayList类,怎么实现呢?

方法1:【成功,但是删除的时候会改变list的index索引和size大小,可能会在遍历时导致一些访问越界的问题,因此不是特别推荐】

[java] view plain copy
1. public class Main {    
2.     public static void main(String[] args) throws Exception {    
3.         List<Integer> list = new ArrayList<>();    
4.         for (int i = 0; i < 5; i++)    
5.             list.add(i);    
6.         // list {0, 1, 2, 3, 4}    
7.         for (int i = 0; i < list.size(); i++) {    
8.             // index and number    
9.             System.out.print(i + " " + list.get(i));    
10.             if (list.get(i) % 2 == 0) {    
11.                 list.remove(list.get(i));    
12.                 System.out.print(" delete");    
13.                 i--; // 索引改变!  不然输出结果与预期不符   
14.             }    
15.             System.out.println();    
16.         }    
17.     }    
18. }

方法2:【报异常,第一个会被正常删除,之后调用remove() 时,会报java.util.C_M_E异常】

[java] view plain copy
1. public class Main {    
2.     public static void main(String[] args) throws Exception {    
3.         List<Integer> list = new ArrayList<>();    
4.         for (int i = 0; i < 5; i++)    
5.             list.add(i);    
6.         // list {0, 1, 2, 3, 4}    
7.         for (Integer num : list) {    
8.             // index and number    
9.             System.out.print(num);    
10.             if (num % 2 == 0) {    
11.                 list.remove(num);    
12.                 System.out.print(" delete");    
13.             }    
14.             System.out.println();    
15.         }    
16.     }    
17. }

方法3:【成功,调用Iterator的remove()方法,而不是内部类的集合的remove()】

[java] view plain copy
1. public class Main {    
2.     public static void main(String[] args) throws Exception {    
3.         List<Integer> list = new ArrayList<>();    
4.         for (int i = 0; i < 5; i++)    
5.             list.add(i);    
6.         // list {0, 1, 2, 3, 4}    
7.         Iterator<Integer> it = list.iterator();    
8.         while (it.hasNext()) {    
9.             // index and number    
10.             int num = it.next();    
11.             System.out.print(num);    
12.             if (num % 2 == 0) {    
13.                 it.remove();    
14.                 System.out.print(" delete");    
15.             }    
16.             System.out.println();    
17.         }    
18.     }    
19. }

那么对于线程安全的 CopyOnWriteArrayList类呢?

 

方法1:【与ArrayList一样】

[java] view plain copy
1. public class Main {    
2.     public static void main(String[] args) throws Exception {    
3.         List<Integer> list = new CopyOnWriteArrayList<>();    
4.         for (int i = 0; i < 5; i++)    
5.             list.add(i);    
6.         // list {0, 1, 2, 3, 4}    
7.         for (int i = 0; i < list.size(); i++) {    
8.             // index and number    
9.             System.out.print(i + " " + list.get(i));    
10.             if (list.get(i) % 2 == 0) {    
11.                 list.remove(list.get(i));    
12.                 System.out.print(" delete");    
13.                 i--; // 索引改变!    
14.             }    
15.             System.out.println();    
16.         }    
17.     }    
18. }

方法2:【成功,与ArrayList不同,因为copy---List的设计保证他能避免 C_M_E异常的报出】

[java] view plain copy
1. public class Main {    
2.     public static void main(String[] args) throws Exception {    
3.         List<Integer> list = new CopyOnWriteArrayList<>();    
4.         for (int i = 0; i < 5; i++)    
5.             list.add(i);    
6.         // list {0, 1, 2, 3, 4}    
7.         for (Integer num : list) {    
8.             // index and number    
9.             System.out.print(num);    
10.             if (num % 2 == 0) {    
11.                 list.remove(num);    
12.                 System.out.print(" delete");    
13.             }    
14.             System.out.println();    
15.         }    
16.     }    
17. }

方法3:【报错,java.lang.UnsupportedOperationException 】

A:与ArrayList不同,由于CopyOnWriteArrayList的iterator是对其List的一个“快照”,因此是不可改变的,所以无法使用iterator遍历删除。

[java] view plain copy
1. public class Main {    
2.     public static void main(String[] args) throws Exception {    
3.         List<Integer> list = new CopyOnWriteArrayList<>();    
4.         for (int i = 0; i < 5; i++)    
5.             list.add(i);    
6.         // list {0, 1, 2, 3, 4}    
7.         Iterator<Integer> it = list.iterator();    
8.         while (it.hasNext()) {    
9.             // index and number    
10.             int num = it.next();    
11.             System.out.print(num);    
12.             if (num % 2 == 0) {    
13.                 it.remove();    
14.                 System.out.print(" delete");    
15.             }    
16.             System.out.println();    
17.         }    
18.     }    
19. }

综上,当使用ArrayList时,我们可以使用iterator实现遍历删除;而当我们使用CopyOnWriteArrayList时,我们直接使用增强型for循环遍历删除即可,此时使用iterator遍历删除反而会出现问题。

 

补充 :

       java中,List在遍历的时候,如果被修改了会抛出java.util.ConcurrentModificationException错误。
eg:  主线程遍历list时,子线程向list添加元素。如果想保证遍历的同时向list添加元素呢?CopyOnWriteArrayList 。
Q:   CopyOnWriteArrayList  是可以保证线程安全的呢,为什么?
A:   CopyOnWriteArrayList里处理写操作(包括add、remove、set等)是先将原始的数据通过JDK1.6的Arrays.copyof()来生成一份新的数组,然后在新的数据对象上进行写,写完后再将原来的引用指向到当前这个数据对象(这里应用了常识1),这样保证了每次写都是在新的对象上(因为要保证写的一致性,这里要对各种写操作要加一把锁,JDK1.6在这里用了重入锁)。
      这样读操作就很快很安全,适合在多线程里使用,绝对不会发生ConcurrentModificationException 。
结论:  CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。
另一个避免添加同步代码但可以避免并发修改问题的方式,在调度任务中构建一个新的列表,然后将原来指向到列表上的引用赋值给新的列表。

为了更好理解ArrayList 的遍历同时删除元素,再加一个实例。

[java] view plain copy
1.  public static void main(String[] args) {  
2.     ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));  
3.     for(inti=0;i<list.size();i++){  
4.            list.remove(i);  
5.     }  
6.     System.out.println(list);// [b,d]输出,与预期不一样  
7. }  
[java] view plain copy
1. public static void main(String[] args) {  
2.       ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","a", "b", "c", "d"));  
3.       for (int i = 0; i < list.size(); i++) {  
4.           if (list.get(i).equals("a")) {  
5.               list.remove(i);  
6.           }  
7.       }  
8.       System.out.println(list);//[a,b,c,d]与预期不一致  
9.   }

Q: 看上面的for( ; ; )循环,以为for循环使用迭代器实现的? NoNoNo!看下面例子:

[java] view plain copy
1. //会抛出 C_M_E异常  
2.  public static void main(String[] args) {  
3.         ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","a", "b", "c", "d"));  
4.         for(String s:list){  
5.              if(s.equals("a")){  
6.                   list.remove(s);  
7.              }  
8.         }  
9. }  
[java] view plain copy
1.   
[java] view plain copy
1. //输出正确,但是.next()必须在.remove()之前调用。在一个foreach循环中,编译器会使.next()在删除元素之后被调用??判断hasNext()??  
2. //因此就会抛出ConcurrentModificationException异常,  
3.  public static void main(String[] args) {  
4.         ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","a", "b", "c", "d"));  
5.         Iterator<String>  
6.         iter = list.iterator();  
7.         while(iter.hasNext()){  
8.             String s = iter.next();//要先于remove()调用  
9.                 if(s.equals("a")){  
10.                     iter.remove();  
11.                 }  
12.             }  
13. }

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言JAVA频道!

本文由 @职坐标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论
本文作者 联系TA

擅长与学生或家长及时有效沟通

  • 10
    文章
  • 7745
    人气
  • 95%
    受欢迎度

已有19人表明态度,95%喜欢该老师!

进入TA的空间
求职秘籍 直通车
  • 索取资料 索取资料 索取资料
  • 答疑解惑 答疑解惑 答疑解惑
  • 技术交流 技术交流 技术交流
  • 职业测评 职业测评 职业测评
  • 面试技巧 面试技巧 面试技巧
  • 高薪秘笈 高薪秘笈 高薪秘笈
TA的其他文章 更多>>
JAVA语言-类和对象
经验技巧 100% 的用户喜欢
匿名对象,内部类,静态内部类,static关键字-JAVA从入门到精通
经验技巧 0% 的用户喜欢
JAVA语言-String类的常用方法
经验技巧 0% 的用户喜欢
JAVA语言-volatile
经验技巧 0% 的用户喜欢
JAVA从入门到精通-抽象类
经验技巧 0% 的用户喜欢
其他海同师资 更多>>
吕益平
吕益平 联系TA
熟悉企业软件开发的产品设计及开发
孔庆琦
孔庆琦 联系TA
对MVC模式和三层架构有深入的研究
周鸣君
周鸣君 联系TA
擅长Hadoop/Spark大数据技术
范佺菁
范佺菁 联系TA
擅长Java语言,只有合理的安排和管理时间你才能做得更多,行得更远!
程光淼
程光淼 联系TA
精通C、C++等语言、智能芯片开发
经验技巧30天热搜词 更多>>

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程