【Java】集合类 - Collection 接口的三种遍历方法

Posted by 西维蜀黍 on 2019-03-14, Last Modified on 2023-02-28

第一种 - Iterator(迭代器)

Iterator<String> iterator = list.iterator();  
while(iter.hasNext())  
{  
	iter.next();  
}  

这种方式在循环执行过程中会进行数据锁定,性能稍差。

同时,如果你想在进行遍历的过程中,移除 List 中的某个元素,只能调用 iterator.remove 方法,而不能使用 list.remove () 方法,否则会抛出并发修改异常(java.util.ConcurrentModificationException)。

第二种 - foreach

for(String tmp:list)  
{  
	//do something
}  

foreach 只是 Java 提供的语法糖,内部调用 Iterator(第一种),换汤不换药,因此比 Iterator 稍慢。

第三种 - get (i)

int listSize = list.size();
for (int i = 0; i < listSize; i++) {
    list.get(i);
}

内部不锁定,效率最高,但是当写多线程时要考虑并发操作的问题。

Iterator(迭代器)的使用 - Fail fast 问题

前面我们提到了,如果你想在使用 Iterator(迭代器)进行遍历的过程中,移除 List 中的某个元素,只能调用 iterator.remove 方法,而不能调用 list.remove () 方法,否则一定会抛出并发修改异常(java.util.ConcurrentModificationException)。

注意,当这个异常被抛出时,并不意味着这个 list 一定正在被多个线程同时使用,而在同一个线程中既一边遍历 list,一边调用 list.remove () 方法,也会抛出 ConcurrentModificationException。

解决办法

可以使用 CopyOnWriteArrayList 来替换 ArrayList。

CopyOnWriteArrayList 为何物?

CopyOnWriteArrayList 是 ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。

因此,该类产生的开销比较大,但是在两种情况下,它非常适合使用:

  1. 在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时。
  2. 当遍历操作的数量大大超过可变操作的数量时。

遇到这两种情况使用 CopyOnWriteArrayList 来替代 ArrayList 再适合不过了。

那么,为什么 CopyOnWriterArrayList 可以替代 ArrayList 呢?

  • CopyOnWriterArrayList 的无论是从数据结构、定义都和 ArrayList 一样。它和 ArrayList 一样,同样是实现 List 接口,底层使用数组实现。在方法上也包含 add、remove、clear、iterator 等方法。
  • CopyOnWriterArrayList 根本就不会产生 ConcurrentModificationException 异常,也就是它使用迭代器完全不会产生 fail-fast 机制。

Reference