开发中,常有场景:遍历集合,依次判断是否符合条件,如符合条件则删除当前元素。

不知不觉中,有些陷阱,不知你有没有犯。

一、漏网之鱼-for循环递增下标方式遍历集合,并删除元素

如果你用for循环递增下标方式遍历集合,在遍历过程中删除元素,你可能会遗漏了某些元素。说那么说可能也说不清楚,看以下示例:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. public class ListTest_Unwork {
  5.  
  6. public static void main(String[] args) {
  7. List<String> list = new ArrayList<String>();
  8. list.add("1");
  9. list.add("2");
  10. list.add("3");
  11. list.add("4");
  12. list.add("5");
  13. System.out.println("Original list : " + list);
  14.  
  15. String temp = null;
  16. for (int i = 0; i < list.size(); i++) {
  17. temp = list.get(i);
  18.  
  19. System.out.println("Check for " + temp);
  20. if ("3".equals(temp)) {
  21. list.remove(temp);
  22. }
  23. }
  24. System.out.println("Removed list : " + list);
  25. }
  26.  
  27. }

日志打印:

  1. Original list : [1, 2, 3, 4, 5]
  2. Check for 1
  3. Check for 2
  4. Check for 3
  5. Check for 5
  6. Removed list : [1, 2, 4, 5]

如日志所见,其中值为4的元素并未经过判断,漏网之鱼。

解决方法为以下两个(但一般不建议我们在遍历中用不是遍历本身的函数删除元素,见下节关于“ConcurrentModificationException”的内容):

1、对于此情况,我一般都从后面开始遍历,以避免问题:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. public class ListTest_Work {
  5.  
  6. public static void main(String[] args) {
  7. List<String> list = new ArrayList<String>();
  8. list.add("1");
  9. list.add("2");
  10. list.add("3");
  11. list.add("4");
  12. list.add("5");
  13. System.out.println("Original list : " + list);
  14. System.out.println();
  15.  
  16. String temp = null;
  17. for (int i = list.size() - 1; i >= 0; i--) {
  18. temp = list.get(i);
  19.  
  20. System.out.println("Check for " + temp);
  21. if ("3".equals(temp)) {
  22. list.remove(temp);
  23. }
  24. }
  25. System.out.println("Removed list : " + list);
  26. }
  27.  
  28. }

2、直接从新创建一个集合,重新摆放,但消耗内存,慎用:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. public class ListTest_Work2 {
  5.  
  6. public static void main(String[] args) {
  7. List<String> list = new ArrayList<String>();
  8. list.add("1");
  9. list.add("2");
  10. list.add("3");
  11. list.add("4");
  12. list.add("5");
  13. System.out.println("Original list : " + list);
  14. System.out.println();
  15.  
  16. List<String> tempList = new ArrayList<String>();
  17. for (String temp : list) {
  18. System.out.println("Check for " + temp);
  19. if (!"3".equals(temp)) {
  20. tempList.add(temp);
  21. }
  22. }
  23. System.out.println("Removed list : " + tempList);
  24. }
  25.  
  26. }

二、ConcurrentModificationException异常-Iterator遍历集合过程中用其他手段(或其他线程)操作元素

ConcurrentModificationException是Java集合的一个快速报错(fail-fast)机制,防止多个线程同时修改同一个集合的元素。在用Iterator遍历集合时,如果你用其他手段(非Iterator自身手段)操作集合元素,就会报ConcurrentModificationException。

不信?用Iterator方式或简写的for(Object o : list) {}方式,遍历集合,修改元素时会报异常:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. public class ListTest2_Unwork {
  5.  
  6. public static void main(String[] args) {
  7. List<String> list = new ArrayList<String>();
  8. list.add("1");
  9. list.add("2");
  10. list.add("3");
  11. list.add("4");
  12. list.add("5");
  13. System.out.println("Original list : " + list);
  14. System.out.println();
  15.  
  16. for (String temp : list) {
  17. System.out.println("Check for " + temp);
  18. if ("3".equals(temp)) {
  19. list.remove(temp);
  20. }
  21. }
  22. System.out.println("Removed list : " + list);
  23. }
  24.  
  25. }

  1. import java.util.ArrayList;
  2. import java.util.Iterator;
  3. import java.util.List;
  4.  
  5. public class ListTest3_Unwork {
  6.  
  7. public static void main(String[] args) {
  8. List<String> list = new ArrayList<String>();
  9. list.add("1");
  10. list.add("2");
  11. list.add("3");
  12. list.add("4");
  13. list.add("5");
  14. System.out.println("Original list : " + list);
  15. System.out.println();
  16.  
  17. Iterator<String> i = list.iterator();
  18. String temp = null;
  19. while (i.hasNext()) {
  20. temp = i.next();
  21. System.out.println("Check for " + temp);
  22. if ("3".equals(temp)) {
  23. list.remove(temp);
  24. }
  25. }
  26. System.out.println("Removed list : " + list);
  27. }
  28.  
  29. }

日志:

  1. Original list : [1, 2, 3, 4, 5]
  2.  
  3. Check for 1
  4. Check for 2
  5. Check for 3
  6. Exception in thread "main" java.util.ConcurrentModificationException
  7. at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  8. at java.util.ArrayList$Itr.next(ArrayList.java:831)
  9. at ListTest3_Unwork.main(ListTest3_Unwork.java:20)

在删除元素“3”时,会报异常。

对于此情况,需要用iterator的remove方法替代,结果是妥妥的:

  1. import java.util.ArrayList;
  2. import java.util.Iterator;
  3. import java.util.List;
  4.  
  5. public class ListTest3_Work {
  6.  
  7. public static void main(String[] args) {
  8. List<String> list = new ArrayList<String>();
  9. list.add("1");
  10. list.add("2");
  11. list.add("3");
  12. list.add("4");
  13. list.add("5");
  14. System.out.println("Original list : " + list);
  15. System.out.println();
  16.  
  17. Iterator<String> i = list.iterator();
  18. String temp = null;
  19. while (i.hasNext()) {
  20. temp = i.next();
  21. System.out.println("Check for " + temp);
  22. if ("3".equals(temp)) {
  23. i.remove();
  24. }
  25. }
  26. System.out.println("Removed list : " + list);
  27. }
  28.  
  29. }

延伸个小问题,为什么for(Object o : list) {}方式遍历集合,现象和Iterator方式一样,都会报错呢?

答:这是因为Java的糖语法,“for(Object o : list) {}方式”只是Java语言用“易用性糖衣”吸引你的手段,本质上,它也是Iterator。不信,你写下下面这段程序,反编译看看就清楚了:

  1. package com.nichagil.test.forloop;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. public class ForTester {
  7.  
  8. public static void main(String[] args) {
  9. List<String> list = new ArrayList<String>();
  10. list.add("a");
  11.  
  12. for (String s : list) {
  13. list.remove(s);
  14. System.out.println(s);
  15. }
  16. }
  17.  
  18. }

反编译后是这样的:

  1. package com.nichagil.test.forloop;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Iterator;
  5.  
  6. public class ForTester {
  7. public static void main(String[] args) {
  8. ArrayList list = new ArrayList();
  9. list.add("a");
  10. Iterator arg2 = list.iterator();
  11.  
  12. while (arg2.hasNext()) {
  13. String s = (String) arg2.next();
  14. list.remove(s);
  15. System.out.println(s);
  16. }
  17.  
  18. }
  19. }

【Java】集合(List、Set)遍历、判断、删除元素时的小陷阱的更多相关文章

  1. Java中ArrayList循环遍历并删除元素的陷阱

    ava中的ArrayList循环遍历并且删除元素时经常不小心掉坑里,昨天又碰到了,感觉有必要单独写篇文章记一下. 先写个测试代码: import java.util.ArrayList; public ...

  2. 集合--(List、Set、Map)遍历、删除、比较元素时的小陷阱

    6,Map集合遍历的4中方法? 5,List遍历时如何remove元素 4.漏网之鱼-for循环递增下标方式遍历集合,并删除元素 如果你用for循环递增下标方式遍历集合,在遍历过程中删除元素,你可能会 ...

  3. Java HashMap 如何正确遍历并删除元素

    (一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K ...

  4. JAVA List 一边遍历一边删除元素

    JAVA List 一边遍历一边删除元素,报java.util.ConcurrentModificationException异常 2015年02月10日 14:42:49 zhanzkw 阅读数:3 ...

  5. Java集合01----ArrayList的遍历方式及应用

                                                 Java集合01----ArrayList的遍历方式及应用 前面已经学习了ArrayList的源代码,为了学以 ...

  6. 【转】ArrayList循环遍历并删除元素的常见陷阱

    转自:https://my.oschina.net/u/2249714/blog/612753?p=1 在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出b ...

  7. ArrayList循环遍历并删除元素的常见陷阱

    在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出bug.不妨把这个问题当做一道面试题目,我想一定能难道不少的人.今天就给大家说一下在ArrayList循环 ...

  8. ArrayList循环遍历并删除元素的几种情况

    如下代码,想要循环删除列表中的元素b,该怎么处理? public class ListDemo { public static void main(String[] args) { ArrayList ...

  9. java集合 collection-list-ArrayList 将自定义对象作为元素存到ArrayList集合中,并去除重复元素。

    import java.util.*; /* 将自定义对象作为元素存到ArrayList集合中,并去除重复元素. 比如:存人对象.同姓名同年龄,视为同一个人.为重复元素. 思路: 1,对人描述,将数据 ...

随机推荐

  1. JavaOOP QuickHit项目分析

    项目需求:游戏等级6级,随机字符串每级长度不同.每升一级减少比较次数,但是字符串长度相应增加!每级总分数不同,如果游戏中途输入错误则游戏退出!玩家每次在规定时间内输入字符串的同时,打印出游戏难度等级. ...

  2. POJ 2947 Widget Factory(高斯消元)

    Description The widget factory produces several different kinds of widgets. Each widget is carefully ...

  3. 。。。HibernateTemplate与Session。。。

    今天在学习Spring框架的时候,突然发现了这个类----HibernateTemplate,这个类与Session一开始认为是差不多的,这个HibernateTemplate类对象拥有Session ...

  4. bean在容器上的生命周期

            初始化两种方法:         1,使用init-method属性指定那个方法在bean依赖关系设置好后自动执行.         2,实现initializingBean接口 实现 ...

  5. android复习第一天-----简单的android常识

    前言:要去面试了,这些天花一些事件把android中简单的知识点来串联的复习一下 1,android中的工程结构 src文件夹:存储android文件的源代码 gen文件夹:有工具自动生成,不要去修改 ...

  6. 02---Net基础加强

    将普通日期格式:“2014年7月8日”转换成汉字日期格式:“二零一四年七月八日”.(暂时不考虑10日,13日,23日) class Program { static void Main(string[ ...

  7. 三层架构和MVC模式

    目录[-] 1.三层架构 2.MVC 2.1 标准的MVC(Model-View-Controller) 2.2 Web MVC 3.三层架构和MVC的区别与联系 1.三层架构 三层架构(3-tier ...

  8. SQLServer查询速度慢的原因

    查询速度慢的原因很多,常见如下几种:  1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应.  3.没有创建计算列导致查询不优化.  4.内存 ...

  9. android textView 添加超链接(两种实现方式)

    在textView添加超链接,有两种方式,第一种通过HTML格式化你的网址,一种是设置autolink,让系统自动识别超链接,下面为大家介绍下这两种方法的实现 在textView添加超链接,有两种方式 ...

  10. django migrate10060 Duplicate column name错误

    这个错误意思是有重复的列名,其实大部分原因是因为某些列被执行了多次,可以打开migration里面的django生成的文件去排查错误,然后自己手动修改数据库还原,实在不行可以把除了0001和init文 ...