常见的遍历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 list = new ArrayList<>();
4.         for (int i = 0; i 
5.             list.add(i);
6.         // list {0, 1, 2, 3, 4}
7.         for (int i = 0; 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 list = new ArrayList<>();
4.         for (int i = 0; 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 list = new ArrayList<>();
4.         for (int i = 0; i 
5.             list.add(i);
6.         // list {0, 1, 2, 3, 4}
7.         Iterator 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 list = new CopyOnWriteArrayList<>();
4.         for (int i = 0; i 
5.             list.add(i);
6.         // list {0, 1, 2, 3, 4}
7.         for (int i = 0; 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 list = new CopyOnWriteArrayList<>();
4.         for (int i = 0; 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 list = new CopyOnWriteArrayList<>();
4.         for (int i = 0; i 
5.             list.add(i);
6.         // list {0, 1, 2, 3, 4}
7.         Iterator 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 list = new ArrayList(Arrays.asList("a","b","c","d"));
3.     for(inti=0;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 list = new ArrayList(Arrays.asList("a","a", "b", "c", "d"));
3.       for (int i = 0; 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 list = new ArrayList(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 list = new ArrayList(Arrays.asList("a","a", "b", "c", "d"));
5.         Iterator
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. }

本文由职坐标整理并发布,希望对同学们有所帮助。