Java集合遍历引发的"血案"

一、List集合迭代方式遍历一

<1>、可能出现的问题一:出现并发修改异常(ConcurrentModificationException)

import java.util.ArrayList;  
import java.util.Iterator;  
import java.util.List;

public class Test  {  
    private static List<String> list = new ArrayList<String>();  
    public static void init(int num)   {  
        for (int i = 0; i < num; i++)   {  
            list.add(i + "");  
        }  
    }  
  
    @SuppressWarnings("all")  
    public static void main(String[] args)   {  
        int num = 5;  
        init(num);  
        for (Iterator iterator = list.iterator(); iterator.hasNext();)  {  
            String string = (String) iterator.next();  
            if (string.equals(num - 1 + ""))   {  
                System.out.println("执行remove操作");  
                list.remove(string);  
            }   else   {  
                System.out.println(string);  
            }  
        }  
       }  
}

上述运行结果如下:


 上述示例出现ConcurrentModificationException异常的原因在于不能对list集合同时进行读写操作。

<2>、可能出现的情况二:下标/游标错位

import java.util.ArrayList;  
import java.util.Iterator;  
import java.util.List;

public class Test  {  
    private static List<String> list = new ArrayList<String>();  
    public static void init(int num)  {  
        for (int i = 0; i < num; i++)  {  
            list.add(i + "");  
        }  
    }  
  
    @SuppressWarnings("all")  
    public static void main(String[] args)   {  
        int num = 5;  
        init(num);  
        for (Iterator iterator = list.iterator(); iterator.hasNext();)   {  
            String string = (String) iterator.next();  
            if (string.equals(num - 2 + ""))  {  
                System.out.println("执行remove操作");  
                list.remove(string);  
            }  else  {  
                System.out.println(string);  
            }  
        }  
    }  
}

上述运行结果如下:


上述结果显然是有问题的,我们认为的正确结果应该是:

0
1
2
执行remove操作

4

那为什么没有打印4呢?原因:当if条件成立时,也就是string等于3时,执行移除操作,下标为3的元素移除后,那下标为4的元素前移一位,就刚好满足跳出循环的条件,因为游标在3的位置,前移后下标为4的位置就没有元素了,这时结束循环,直接跳过了4这个元素。

二、List集合迭代方式遍历二

<1>、可能出现的问题一:出现并发修改异常(ConcurrentModificationException)

import java.util.ArrayList;  
import java.util.List;

public class Test  {  
    private static List<String> list = new ArrayList<String>();  
    public static void init(int num)   {  
        for (int i = 0; i < num; i++)  {  
            list.add(i + "");  
        }  
    }  
  
    @SuppressWarnings("all")  
    public static void main(String[] args)   {  
        int num = 5;  
        init(num);  
        for (String string : list)   {  
            if (string.equals(num - 1 + ""))  {  
                System.out.println("执行remove操作");  
                list.remove(string);  
            }   else  {  
                System.out.println(string);  
            }  
        }  
    }  
}

上述执行结果如下:

<2>、可能出现的情况二:下标/游标错位

import java.util.ArrayList;  
import java.util.List;

public class Test  {  
    private static List<String> list = new ArrayList<String>();  
    public static void init(int num)  {  
        for (int i = 0; i < num; i++)   {  
            list.add(i + "");  
        }  
    }  
  
    @SuppressWarnings("all")  
    public static void main(String[] args)   {  
        int num = 5;  
        init(num);  
        for (String string : list)   {  
            if (string.equals(num - 2 + ""))   {  
                System.out.println("执行remove操作");  
                list.remove(string);  
            }  else   {  
                System.out.println(string);  
            }  
        }   
    }  
}

上述运行结果如下:

很容易看出,上面两个示例跟第一种迭代方式的结果是一样的,那可能就会有人说,这个两个不是增强for循环嘛,怎么成了迭代了?其实增强for循环底层实现就是走迭代的方式。所以结果一样没什么奇怪的,它们出错的原理也是一样的,我这里就不多说了。

三、List非迭代方式遍历

import java.util.ArrayList;  
import java.util.List;

public class Test {  
    private static List<String> list = new ArrayList<String>();  
    public static void init(int num)   {  
        for (int i = 0; i < num; i++)  {

list.add(i + "");  
        }  
    }  
  
    @SuppressWarnings("all")  
    public static void main(String[] args)  {  
        int num = 5;  
        init(num);  
        for (int i = 0; i < list.size(); i++)   {  
            String string = list.get(i);  
            if (string.equals(num - 2 + ""))  {  
                System.out.println("执行remove操作");  
                list.remove(string);  
            }   else  {  
                System.out.println(string);  
            }  
        }  
    }  
}

上述执行结果如下:


 从结果可以看出跟方式一和方式二的第二种情况是一样的,还是会造成下标错位,原理一样,我这里就不说了,但是不会出现并发修改异常。

四、解决方案

讲了怎么多了,该讲讲怎么解决该问题了,解决该问题的方式有多种,我下面介绍两种解决方案。

<1>、使用并发库(java.util.concurrent)下的CopyOnWriteArrayList类可以解决该问题,但是性能开销很大。

import java.util.List;  
import java.util.concurrent.CopyOnWriteArrayList;

public class Test  {    
    private static List<String> list = new CopyOnWriteArrayList<String>();  
    public static void init(int num)  {  
        for (int i = 0; i < num; i++)  {  
            list.add(i + "");  
        }  
    }  
  
    @SuppressWarnings("all")  
    public static void main(String[] args)    {  
        int num = 5;  
        init(num);          
        for (String string : list)  {  
            if (string.equals(num - 1 + ""))  
            // if (string.equals(num - 2 + ""))  
            {  
                System.out.println("执行remove操作");  
                list.remove(string);  
            } else  {  
                System.out.println(string);  
            }  
        }  
    }  
}

上面代码可以自己复制到你的编译器中执行查看结果,我这里就不把执行结果贴出来了。

<2>、先使用一个临时的List集合来存放需要移除的元素,最后使用removeAll方法来移除所有。

import java.util.ArrayList;  
import java.util.List;

public class TestDemo  {  
    private static List<String> list = new ArrayList<String>();  
    public static void init(int num) {  
        for (int i = 0; i < num; i++)  {  
            list.add(i + "");  
        }  
    }  
  
    @SuppressWarnings("all")  
    public static void main(String[] args)   {  
        int num = 5;  
        init(num);  
        List<String> delList = new ArrayList<String>();  
        for (String string : list)  {  
             if (string.equals(num - 1 + ""))   {  
                delList.add(string);  
            }   else  {  
                System.out.println(string);  
            }  
        } 
        list.removeAll(delList);  
  
    }  
}

上面代码可以自己复制到你的编译器中执行查看结果,我这里就不把执行结果贴出来了。

最后讲一点Set集合出现的错误情况以及解决方案是类似的,要懂得举一反三,Set集合对应CopyOnWriteArraySet类。

时间: 2024-08-09 13:16:43

Java集合遍历引发的"血案"的相关文章

java 集合遍历时删除元素

本文探讨集合在遍历时删除其中元素的一些注意事项,代码如下 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import java.util.ArrayList; import java.util.Iterator; import java

Java集合遍历时删除

public static void main(String[] args){ List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); Iterator<Integer> interator = list.iterator(); while(interator.hasNext()){ Integer i

Java集合遍历性能

数据在内存中主要有两种存储方式: 1.顺序存储,Random Access(Direct Access) 这种方式,相邻的数据元素存放于相邻的内存地址中,整块内存地址是连续的,可以根据元素的位置直接计算出内存地址,直接进行读取.读取一个特定位置元素的平均时间复杂度为O(1).正常来说,只有基于数组实现的集合,才有这种特性.Java中以ArrayList为代表. 2.链式存储,Sequential Access: 这种方式,每一个数据元素,在内存中都不要求处于相邻的位置,每个数据元素包含他的下一个

java集合遍历删除指定元素异常分析总结

在使用集合的过程中,我们经常会有遍历集合元素,删除指定的元素的需求,而对于这种需求我们往往使用会犯些小错误,导致程序抛异常或者与预期结果不对,本人很早之前就遇到过这个坑,当时没注意总结,结果前段时间又遇到了这个问题,因此,总结下遍历集合的同时如何删除集合中指定的元素: 1.错误场景复原 public class ListRemoveTest { public static void main(String[] args) { List<User> users = new ArrayList&l

java集合遍历的几种方式总结及比较

集合类的通用遍历方式, 用迭代器迭代: Iterator it = list.iterator(); while(it.hasNext()) { Object obj = it.next(); } Map遍历方式: 1.通过获取所有的key按照key来遍历 //Set<Integer> set = map.keySet(); //得到所有key的集合 for (Integer in : map.keySet()) { String str = map.get(in);//得到每个key多对用v

JAVA集合遍历

一.Set public class text { public static void main(String[] args) { Set<String> a = new HashSet<String>(); a.add("aa"); a.add("bb"); a.add("cc"); a.add("dd"); a.add("ee"); a.add("ff");

JVM--JVM finalize实现原理与由此引发的血案

原创内容,转载请注明出处 本文由一桩因为使用了JAVA finalize()而引发的血案入手,讲解了JVM中finalize()的实现原理和它的陷阱所在,希望能够对广大JAVA开发者起到一点警示作用.除此之外,本文从实际问题出发,描述了解决问题的过程和方法.如写模拟程序来重现问题,使用jmap工具进行分析等,希望对大家提供借鉴. 本文分三个章节,先介绍实际项目中遇到的问题,随后介绍了问题重现和分析方法,最后对问题的元凶,override finalize()的实现原理和陷阱进行了讲解和介绍.篇幅

Java 集合

在Java Collections Framework中,不同类型的集合使用不同类型的数据结构以不同的方式存储它们的元素. 集合框架提供了遍历集合的以下方法: 使用迭代器 使用for-each循环 使用forEach()方法 使用迭代器 迭代器可以对集合执行以下三个操作: 检查是否有尚未访问的元素. hasNext() 检查是否有下一个访问的元素. next() 删除集合的最后访问元素. remove() 例子1 使用迭代器打印列表的所有元素: import java.util.ArrayLis

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

                                             Java集合01----ArrayList的遍历方式及应用 1.ArrayList的遍历方式 a.一般for循环(随机访问) Integer value = null; int size = list.size(); for (int i=0; i<size; i++) { value = (Integer)list.get(i); } b.增强型for循环(for-each) Integer value